API CRUD Operations
Open source
API CRUD Operations
The examples showcase the testing of CRUD operations on a REST API.
CRUD refers to the basic operations in a database: Create, Read, Update, and Delete. We can map these operations to HTTP methods in REST APIs:
- Create: HTTP
POST
operation to create a new resource. - Read: HTTP
GET
to retrieve a resource. - Update: HTTP
PUT
orPATCH
to change an existing resource. - Delete: HTTP
DELETE
to remove a resource.
This document has two examples, one that uses the core k6 APIs (k6/http
and checks
) and another to show the more recent APIs httpx
and k6chaijs
).
Test steps
In the setup() stage we create a user for QuickPizza. We then retrieve and return a bearer token to authenticate the next CRUD requests.
The steps implemented in the VU stage are as follows:
- Create a new resource, a pizza rating.
- Read the list of ratings.
- Update the rating’s stars (e.g. to 5 stars) and read the rating to confirm the update operation.
- Delete the rating resource.
Core k6 APIs example
JavaScript
import http from 'k6/http';
import { check, group, fail } from 'k6';
export const options = {
vus: 1,
iterations: 1,
};
// Create a random string of given length
function randomString(length, charset = '') {
if (!charset) charset = 'abcdefghijklmnopqrstuvwxyz';
let res = '';
while (length--) res += charset[(Math.random() * charset.length) | 0];
return res;
}
const USERNAME = `${randomString(10)}@example.com`; // Set your own email or `${randomString(10)}@example.com`;
const PASSWORD = 'secret';
const BASE_URL = 'https://quickpizza.grafana.com';
// Register a new user and retrieve authentication token for subsequent API requests
export function setup() {
const res = http.post(
`${BASE_URL}/api/users`,
JSON.stringify({
username: USERNAME,
password: PASSWORD,
})
);
check(res, { 'created user': (r) => r.status === 201 });
const loginRes = http.post(
`${BASE_URL}/api/users/token/login`,
JSON.stringify({
username: USERNAME,
password: PASSWORD,
})
);
const authToken = loginRes.json('token');
check(authToken, { 'logged in successfully': () => authToken.length > 0 });
return authToken;
}
export default function (authToken) {
// set the authorization header on the session for the subsequent requests
const requestConfigWithTag = (tag) => ({
headers: {
Authorization: `Bearer ${authToken}`,
},
tags: Object.assign(
{},
{
name: 'PrivateRatings',
},
tag
),
});
let URL = `${BASE_URL}/api/ratings`;
group('01. Create a new rating', () => {
const payload = {
stars: 2,
pizza_id: 1, // Pizza ID 1 already exists in the database.
};
const res = http.post(URL, JSON.stringify(payload), requestConfigWithTag({ name: 'Create' }));
if (check(res, { 'Rating created correctly': (r) => r.status === 201 })) {
URL = `${URL}/${res.json('id')}`;
} else {
console.log(`Unable to create rating ${res.status} ${res.body}`);
return;
}
});
group('02. Fetch my ratings', () => {
const res = http.get(`${BASE_URL}/api/ratings`, requestConfigWithTag({ name: 'Fetch' }));
check(res, { 'retrieve ratings status': (r) => r.status === 200 });
check(res.json(), { 'retrieved ratings list': (r) => r.ratings.length > 0 });
});
group('03. Update the rating', () => {
const payload = { stars: 5 };
const res = http.put(URL, JSON.stringify(payload), requestConfigWithTag({ name: 'Update' }));
const isSuccessfulUpdate = check(res, {
'Update worked': () => res.status === 200,
'Updated stars number is correct': () => res.json('stars') === 5,
});
if (!isSuccessfulUpdate) {
console.log(`Unable to update the rating ${res.status} ${res.body}`);
return;
}
});
group('04. Delete the rating', () => {
const delRes = http.del(URL, null, requestConfigWithTag({ name: 'Delete' }));
const isSuccessfulDelete = check(null, {
'Rating was deleted correctly': () => delRes.status === 204,
});
if (!isSuccessfulDelete) {
console.log('Rating was not deleted properly');
return;
}
});
}
httpx and k6chaijs example
JavaScript
import { describe, expect } from 'https://jslib.k6.io/k6chaijs/4.3.4.3/index.js';
import { Httpx } from 'https://jslib.k6.io/httpx/0.1.0/index.js';
import {
randomIntBetween,
randomItem,
randomString,
} from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
export const options = {
// for the example, let's run only 1 VU with 1 iteration
vus: 1,
iterations: 1,
};
const USERNAME = `user${randomIntBetween(1, 100000)}@example.com`; // Set your own email;
const PASSWORD = 'secret';
const session = new Httpx({ baseURL: 'https://quickpizza.grafana.com' });
// Register a new user and retrieve authentication token for subsequent API requests
export function setup() {
let authToken = null;
describe(`setup - create a test user ${USERNAME}`, () => {
const resp = session.post(
`/api/users`,
JSON.stringify({
username: USERNAME,
password: PASSWORD,
})
);
expect(resp.status, 'User create status').to.equal(201);
expect(resp, 'User create valid json response').to.have.validJsonBody();
});
describe(`setup - Authenticate the new user ${USERNAME}`, () => {
const resp = session.post(
`/api/users/token/login`,
JSON.stringify({
username: USERNAME,
password: PASSWORD,
})
);
expect(resp.status, 'Authenticate status').to.equal(200);
expect(resp, 'Authenticate valid json response').to.have.validJsonBody();
authToken = resp.json('token');
expect(authToken, 'Authentication token').to.be.a('string');
});
return authToken;
}
export default function (authToken) {
// set the authorization header on the session for the subsequent requests
session.addHeader('Authorization', `Bearer ${authToken}`);
describe('01. Create a new rating', (t) => {
const payload = {
stars: 2,
pizza_id: 1, // Pizza ID 1 already exists in the database
};
session.addTag('name', 'Create');
const resp = session.post(`/api/ratings`, JSON.stringify(payload));
expect(resp.status, 'Rating creation status').to.equal(201);
expect(resp, 'Rating creation valid json response').to.have.validJsonBody();
session.newRatingId = resp.json('id');
});
describe('02. Fetch my ratings', (t) => {
session.clearTag('name');
const resp = session.get('/api/ratings');
expect(resp.status, 'Fetch ratings status').to.equal(200);
expect(resp, 'Fetch ratings valid json response').to.have.validJsonBody();
expect(resp.json('ratings').length, 'Number of ratings').to.be.above(0);
});
describe('03. Update the rating', (t) => {
const payload = {
stars: 5,
};
const resp = session.patch(`/api/ratings/${session.newRatingId}`, JSON.stringify(payload));
expect(resp.status, 'Rating patch status').to.equal(200);
expect(resp, 'Fetch rating valid json response').to.have.validJsonBody();
expect(resp.json('stars'), 'Stars').to.equal(payload.stars);
// read rating again to verify the update worked
const resp1 = session.get(`/api/ratings/${session.newRatingId}`);
expect(resp1.status, 'Fetch rating status').to.equal(200);
expect(resp1, 'Fetch rating valid json response').to.have.validJsonBody();
expect(resp1.json('stars'), 'Stars').to.equal(payload.stars);
});
describe('04. Delete the rating', (t) => {
const resp = session.delete(`/api/ratings/${session.newRatingId}`);
expect(resp.status, 'Rating delete status').to.equal(204);
});
}
Was this page helpful?
Related documentation
Related resources from Grafana Labs
Additional helpful documentation, links, and articles:
24 Apr

Getting started with Grafana dashboard design
In this webinar, you'll learn how to design stylish and easily accessible Grafana dashboards that tell a story.
08 May

Grafana 12 deep dive
It wouldn’t be GrafanaCON without a new release. Join us as we dive into the latest features of Grafana 12, including dashboards as code, Git-based configs, dynamic dashboards, and more.
08 May

Monitoring EA App’s 200+ core error metrics with a scalable, color-coded Grafana dashboard
Learn how engineers at Electronic Arts built a Grafana dashboard that client teams can use to monitor 200+ core error metrics.