verdad
v1.0.35
Published
Define a single source of truth for your API and persistence layer, and use Verdad's extensions to a) call it, b) implement it, and c) deploy it.
Downloads
16
Keywords
Readme
Verdad
Verdad lets you:
- ✅ write your API once, with support for multiple base URLs and fully typed parameters & bodies (using io-ts)
- ✅ implement and deploy your API to any infrastructure using built-in or custom extensions
- ✅ call your API from the client with zero duplication and no synchonization headaches
Step 1
Define your backend API in Verdad:
import * as t from 'io-ts'
import { StatusCodes } from "http-status-codes";
import { NumberFromString } from 'io-ts-types';
import { VerdadRESTAPI } from "verdad";
export const musicAPI = VerdadRESTAPI.api({
name: 'my New Music Startup',
servers: {
prod: 'https://api.music.com',
test: 'https://test-api.music.com',
},
builder: (ctx) => ({
playlists: VerdadRESTAPI.resource(ctx, ['users', { parameter: 'userID' }, 'playlists'], {
get: (ctx) => VerdadRESTAPI.method(ctx, {
pathParametersType: t.type({ userID: t.string }),
queryParametersType: t.partial({ pageNumber: NumberFromString }),
headerParametersType: t.type({ 'authorization-token': t.string, }),
requestBodyType: t.null,
successResponse: {
statusCodes: [
StatusCodes.OK as const
],
bodyType: t.array(PlaylistModel)
},
errorResponse: {
statusCodes: [
StatusCodes.UNAUTHORIZED as const,
StatusCodes.BAD_REQUEST as const,
StatusCodes.INTERNAL_SERVER_ERROR as const,
],
bodyType: t.type({ errorDetails: t.string }),
}
}),
post: () => undefined,
delete: () => undefined,
patch: () => undefined,
put: () => undefined,
}),
// albums: ...,
})
})
Step 2
Use an extension to deploy this API to your infrastructure of choice:
For example, to deploy to AWS Lambda:
- Call
VerdadCloudFormation.makeServerlessFunctions()
from yourserverless.ts
file:
import type { AWS } from '@serverless/typescript';
import { VerdadCloudFormation } from 'verdad/lib/extensions';
const serverlessConfig: AWS = {
service: 'music-api',
provider: { name: 'aws' },
functions: VerdadCloudFormation.makeServerlessFunctions(musicAPI),
};
module.exports = serverlessConfig;
- Implement each of your API methods.
makeServerlessFunctions()
looks for the implementations undersrc/resources/<path>/<method>
. For theusers/*/playlists
GET method example above, it would look for an implementation insrc/resources/users/playlists/get.ts
:
export const { verdadMain } = implement(
musicAPI.resources.playlists.get,
async (input) => {
const playlists = retrievePlaylists(
input.pathParameters.userID,
input.headerParameters['authorization-token'],
input.queryParameters.pageNumber,
)
return E.right({
statusCode: StatusCodes.OK,
body: {
value: playlists,
type: t.array(PlaylistModel)
}
})
},
(error: LambdaRuntimeError) => {
error // <-- access error
// Error is a discriminated union; use switch to handle specific error scenarios
return {
statusCode: StatusCodes.INTERNAL_SERVER_ERROR,
body: {
value: { errorDetails: 'Details hidden for security' },
type: t.type({ errorDetails: t.string }),
}
}
}
)
Step 3
Use a client extension to call the APIs:
For example, to make calls using Axios:
import { VerdadAxios } from './extensions/axios/Axios';
async function getPlaylists(userID: string, authToken: string) {
const musicAPIAxios = new VerdadAxios.RESTAPI({
api: musicAPI,
server: 'prod',
debug: {} // Permits logger and custom HTTP agent to support MitM intercepts
})
const playlists = await musicAPIAxios.callMethod(api => api.playlists.get, {
pathParameters: { userID },
queryParameters: {},
headerParameters: {
'authorization-token': authToken
},
body: null,
})
if (E.isRight(playlists)) {
displayToUser(playlists.right.successResponse)
} else {
playlists.left // <-- access error
// Error is a discriminated union; use switch to handle specific error scenarios
console.log("Could not load playlists from backend API")
}
}