Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
]
},
"apis": {
"aggregate->users->get_aggregation": {
"aggregateAPIs->users->id->getAggregation": {
"webhooks": [
{
"url": "https://example.com",
Expand Down
27 changes: 14 additions & 13 deletions src/routes/aggregate/aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,22 @@ export function registerAggregateRoutes(
f => f.supportedAggregation && f.supportedAggregation.length > 0,
);

// construct the api identifier
const apiIdentifier = `aggregate->${model.name}->get_aggregation`;

// extract the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];
// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
const authorization =
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;

// for each aggregatable field, we create a GET route
// /<model_name>/aggregation/<field_name>
for (const field of aggregatableFields) {
// construct the api identifier
const apiIdentifier = `aggregateAPIs->${model.name}->${field.name}->getAggregation`;

// extract the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];
// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
const authorization =
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;

const operations = field.supportedAggregation!;

// generating the schema for the route
Expand All @@ -66,6 +66,7 @@ export function registerAggregateRoutes(
`/${model.name}/aggregation/${field.name}`,
{
schema,
config: {apiIdentifier},
preValidation: async (request, reply) => {
// doing validation here because we need the user for SSP
if (config.auth?.enableAuth && authorization) {
Expand Down
1 change: 1 addition & 0 deletions src/routes/auth/change-password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export function registerChangePasswordRoute(
'/auth/change-password',
{
schema,
config: {apiIdentifier: `authAPIs->${modelName}->all->changePassword`},
preHandler: async (request: FastifyRequest, reply: FastifyReply) => {
try {
await request.jwtVerify();
Expand Down
5 changes: 4 additions & 1 deletion src/routes/auth/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ export function registerLoginRoute(

app.post(
'/auth/login',
{schema},
{
schema,
config: {apiIdentifier: `authAPIs->${modelName}->all->login`},
},
async (request: FastifyRequest<{Body: ModelBody}>, reply: FastifyReply) => {
const {[usernameColumn]: username, [passwordColumn]: password} =
request.body;
Expand Down
5 changes: 4 additions & 1 deletion src/routes/auth/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ export function registerRegistrationRoute(

app.post(
'/auth/register',
{schema},
{
schema,
config: {apiIdentifier: `authAPIs->${modelName}->all->registration`},
},
async (request: FastifyRequest<{Body: ModelBody}>, reply: FastifyReply) => {
const incomingBody = request.body;

Expand Down
3 changes: 2 additions & 1 deletion src/routes/custom-queries/custom-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function registerCustomQueryRoutes(
const bodyProperties: Record<string, object> = {};

// constructing the api identifier
const apiIdentifier = `customAPIs->customQueries->${cq.name}`;
const apiIdentifier = `customAPIs->customQueries->all->${cq.name}`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
Expand Down Expand Up @@ -231,6 +231,7 @@ export function registerCustomQueryRoutes(
method: cq.method,
url: routePath,
schema,
config: {apiIdentifier},
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
Expand Down
28 changes: 14 additions & 14 deletions src/routes/operations/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,21 @@ export function registerDeleteRoutes(
f.supportedOperations?.includes('deletable'),
);

// constructing the api identifier
const apiIdentifier = `modelAPIs->delete->${model.name}`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
const authorization =
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;

// If we have deletable fields, we register a DELETE route for each.
for (const field of deletableFields) {
// constructing the api identifier
const apiIdentifier = `modelAPIs->${model.name}->${field.name}->delete`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
const authorization =
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;
// we map the data type of the field to a JSON schema type for validation
const schema: Record<string, unknown> = generateSchema(
field,
Expand All @@ -63,6 +62,7 @@ export function registerDeleteRoutes(
`/${model.name}/${field.name}/:${field.name}`,
{
schema,
config: {apiIdentifier},
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
Expand Down
29 changes: 15 additions & 14 deletions src/routes/operations/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,20 @@ export function registerEditRoutes(
f.supportedOperations?.includes('editable'),
);

// constructing the api identifier
const apiIdentifier = `modelAPIs->edit->${model.name}`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
const authorization =
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;

for (const field of editableFields) {
// constructing the api identifier
const apiIdentifier = `modelAPIs->${model.name}->${field.name}->edit`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
const authorization =
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;
const isUnique = field.primaryKey || field.unique;
const paramSchema = mapDataTypeToJsonSchema(field.type);

Expand Down Expand Up @@ -230,6 +229,7 @@ export function registerEditRoutes(
`/${model.name}/${field.name}/:${field.name}`,
{
schema: buildRouteSchema('PATCH'),
config: {apiIdentifier},
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
Expand Down Expand Up @@ -268,6 +268,7 @@ export function registerEditRoutes(
`/${model.name}/${field.name}/:${field.name}`,
{
schema: buildRouteSchema('PUT'),
config: {apiIdentifier},
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
Expand Down
3 changes: 2 additions & 1 deletion src/routes/operations/get-all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function registerGetAllRoutes(

for (const model of models) {
// constructing the api identifier
const apiIdentifier = `modelAPIs->getAll->${model.name}`;
const apiIdentifier = `modelAPIs->${model.name}->all->getAll`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
Expand All @@ -59,6 +59,7 @@ export function registerGetAllRoutes(
`/${model.name}/`,
{
schema,
config: {apiIdentifier},
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
Expand Down
28 changes: 14 additions & 14 deletions src/routes/operations/index-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,23 @@ export function registerIndexRoutes(
);
});

// constructing the api identifier
const apiIdentifier = `modelAPIs->index->${model.name}`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
const authorization =
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;

// index apis means for these APIs, we can fetch data using the indexable fields
// for example, if we have a field user_id in the users table, and it is indexed,
// then we can fetch data using the user_id field like /user/<user_id>
for (const field of indexFields) {
// constructing the api identifier
const apiIdentifier = `modelAPIs->${model.name}->${field.name}->index`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
const authorization =
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;
const {
schema,
isUnique,
Expand All @@ -71,6 +70,7 @@ export function registerIndexRoutes(
`/${model.name}/${field.name}/:${field.name}`,
{
schema,
config: {apiIdentifier},
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
Expand Down
3 changes: 2 additions & 1 deletion src/routes/operations/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function registerPostRoutes(

for (const model of models) {
// constructing the api identifier
const apiIdentifier = `modelAPIs->insert->${model.name}`;
const apiIdentifier = `modelAPIs->${model.name}->all->insert`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
Expand All @@ -54,6 +54,7 @@ export function registerPostRoutes(
`/${model.name}/`,
{
schema,
config: {apiIdentifier},
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
Expand Down
26 changes: 13 additions & 13 deletions src/routes/operations/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,20 @@ export function registerSearchRoutes(
f.supportedOperations?.includes('searchable'),
);

// constructing the api identifier
const apiIdentifier = `modelAPIs->search->${model.name}`;

// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];
for (const field of searchableFields) {
// constructing the api identifier
const apiIdentifier = `modelAPIs->${model.name}->${field.name}->search`;

// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
const authorization =
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;
// extracting the api configs based on the api identifier
const webhookConfig = config.apis?.[apiIdentifier]?.webhooks ?? null;
const sspConfig = config.apis?.[apiIdentifier]?.ssp ?? [];

for (const field of searchableFields) {
// calculating the authroization based on auth flag, it can be true
// if the api level auth is enabled, or if the app level auth is enabled
const authorization =
config.apis?.[apiIdentifier]?.authorization ??
config.auth?.enableAuth ??
false;
// defining the primary search query parameter
// for example if the field is "name", this will create "name_search" query parameter
const schema: Record<string, unknown> = generateSchema(
Expand All @@ -66,6 +65,7 @@ export function registerSearchRoutes(
`/${model.name}/search/${field.name}`,
{
schema,
config: {apiIdentifier},
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
Expand Down
2 changes: 2 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ export async function startServer(
? routeOptions.method.join('/')
: routeOptions.method,
url: routeOptions.url,
apiIdentifier: (routeOptions.config as {apiIdentifier?: string})
?.apiIdentifier,
});
});

Expand Down
4 changes: 4 additions & 0 deletions src/types/fastify.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ declare module '@fastify/jwt' {
}

declare module 'fastify' {
interface FastifyContextConfig {
apiIdentifier?: string;
}

interface FastifyInstance {
db: DatabaseQuery;
buildResponse: <T = unknown, R = unknown>(
Expand Down
4 changes: 2 additions & 2 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ export function getAPIFromUniqueIdentifier(
const parts = identifier.split('->');

if (parts[0] === 'customAPIs') {
if (parts[1] === 'customQueries') {
if (parts[1] === 'customQueries' && parts[2] === 'all') {
const customQueries = config?.customAPIs?.customQueries ?? [];

return customQueries.find(cq => cq.name === parts[2]) ?? null;
return customQueries.find(cq => cq.name === parts[3]) ?? null;
}
}

Expand Down
16 changes: 14 additions & 2 deletions src/utils/welcome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {AppConfig} from '@/interfaces/config';
export interface RouteInfo {
method: string;
url: string;
apiIdentifier?: string;
}

const ROCKET_ASCII = `
Expand Down Expand Up @@ -151,7 +152,8 @@ export function showWelcomeScreen(
// Filter routes
const filteredRoutes = routes.filter(route => {
const isHead = route.method.toUpperCase().split('/').includes('HEAD');
const isStatic = route.url.includes('/static');
const isStatic =
route.url.includes('/static') || route.url.includes('/docs');
return !isHead && !isStatic;
});

Expand All @@ -172,7 +174,17 @@ export function showWelcomeScreen(
Math.max(0, maxMethodLength - route.method.length),
);

console.log(' ' + coloredMethod + padding + chalk.white(` ${route.url}`));
const identifier = route.apiIdentifier
? chalk.gray(` [${route.apiIdentifier}]`)
: '';

console.log(
' ' +
coloredMethod +
padding +
chalk.white(` ${route.url}`) +
identifier,
);
});

console.log('\n ' + chalk.gray('─────────────────────────────────────────'));
Expand Down
Loading
Loading