API CRUD Operations
This is documentation for the next version of K6. For the latest stable release, go to the latest version.
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.