Skip to main content

Building APIs

info

In this example, we will design and build a minimal Node.js REST API using openapi-backend and the express framework.

tip

Not using express? The openapi-backend package can be used with any other Node.js framework or server. See boilerplate projects for examples of using OpenAPI stack with other frameworks.

Prerequisites

This guide assumes you already know how to set up a Node.js project with Typescript. You can find a minimal sample project here.

Before starting, make sure to install openapi-backend and express as dependencies:

npm i openapi-backend express

Setting up express

We will start by setting up a basic express server listening on port 9000:

// src/server.ts
import express from 'express';

const app = express();

// use the json middleware
app.use(express.json());

// start server
app.listen(9000, () => console.info('api listening at http://localhost:9000'));

Setting up openapi-backend

We can then import openapi-backend and initialize it with an openapi definition file:

import { OpenAPIBackend } from 'openapi-backend';

const api = new OpenAPIBackend({
definition: './openapi.yml',
});

api.init();

Writing our API spec

We load our API definition from openapi.yml, so let's populate it with a simple API design with a getPets operation:

# src/openapi.yml
openapi: 3.0.2
info:
title: "Pet API"
version: 1.0.0
paths:
"/pets":
get:
operationId: getPets
responses:
"200":
description: list of pets
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Pet"
components:
schemas:
Pet:
type: object
properties:
id:
type: string
type:
type: string
enum: ["cat", "dog"]
name:
type: string
required: ["id", "type"]

Implementing Handlers

Let's then implement an operation handler for the getPets operation defined in our spec.

api.register('getPets', async (c, req: express.Request, res: express.Response) =>
res.status(200).json([{ id: '1', type: 'cat', name: 'Garfield' }])
)

To enable routing and validation, we'll add some default handlers in our code for common exceptions:

// return 400 when request validation fails
api.register('validationFail', (c, req: express.Request, res: express.Response) =>
res.status(400).json({ err: c.validation.errors }),
)
// return 404 when route doesn't match any operation in openapi.yml
api.register('notFound', (c, req: express.Request, res: express.Response) =>
res.status(404).json({ err: 'not found' }),
)

Use as express middleware

Finally we wire up openapi-backend to route, validate and handle API requests with express:

app.use((req, res) => api.handleRequest(req, req, res));

Full Example

Putting everything together, here is our complete example server code:

// src/server.ts
import { OpenAPIBackend, Request } from 'openapi-backend';
import express from 'express';

const api = new OpenAPIBackend({
definition: './openapi.yml',
});

api.init();

// handler for getPets operation in openapi.yml
api.register('getPets', async (c, req: express.Request, res: express.Response) =>
res.status(200).json([{ id: '1', type: 'cat', name: 'Garfield' }])
)
// return 400 when request validation fails
api.register('validationFail', (c, req: express.Request, res: express.Response) =>
res.status(400).json({ err: c.validation.errors }),
)
// return 404 when route doesn't match any operation in openapi.yml
api.register('notFound', (c, req: express.Request, res: express.Response) =>
res.status(404).json({ err: 'not found' }),
)

const app = express();

// use the json middleware
app.use(express.json());

// use openapi-backend to handle requests
app.use((req, res) => api.handleRequest(req as Request, req, res));

// start server
app.listen(9000, () => console.info('api listening at http://localhost:9000'));

Optional: Mocking Responses

Instead of implementing a handler for getPets, you can register a notImplemented handler to mock the response based on the OpenAPI schema:

// mock responses for operations with no registered handlers
api.register('notImplemented', (c, req: express.Request, res: express.Response) => {
const { status, mock } = c.api.mockResponseForOperation(c.operation.operationId);
return res.status(status).json(mock);
});