Skip to content

Commit b6e63f2

Browse files
committed
Fix errors not bubbling
1 parent 2174570 commit b6e63f2

File tree

6 files changed

+118
-77
lines changed

6 files changed

+118
-77
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "api-next",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"repository": "https://github.com/imflavio/api-next",
55
"author": "Flávio Carvalho <[email protected]>",
66
"license": "MIT",

src/hooks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const connectToDatabase = ({
1717
cachedDb[name] = true
1818
}
1919
} catch (err) {
20+
console.error(err)
2021
throw new DatabaseConnectionError()
2122
}
2223
}

src/mongoose.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import { NotFoundError } from './errors/not-found-error'
66
export const createMongooseMethods = (
77
Model: ReturnType<typeof mongoose.model>,
88
): ServiceMethods => ({
9-
find: async () => Model.find(),
9+
find: async (query) => Model.find(query),
1010
create: async (body) => Model.create(body),
11-
get: async (pk) => Model.findById(pk),
12-
update: async (pk, body) => {
13-
const data = await Model.findById(pk)
11+
get: async (pk, query) => Model.findOne({ _id: pk, ...query }),
12+
update: async (pk, body, query) => {
13+
const data = await Model.findOne({ _id: pk, ...query })
1414
if (!data) throw new NotFoundError()
1515

1616
data.set(body)

src/services.ts

Lines changed: 82 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -39,94 +39,106 @@ const getPk = (pk: Pk = {}, query: NextApiRequest['query']): string | null => {
3939
return pk?.cast && value ? pk.cast(value!) : value
4040
}
4141

42-
const getStatusCode = (type: keyof ServiceMethods) => {
43-
switch (type) {
44-
case 'create':
45-
return Status.HTTP_201_CREATED
46-
default:
47-
return Status.HTTP_200_OK
48-
}
49-
}
50-
51-
const runHandler = async (
52-
type: keyof ServiceMethods,
53-
options: ServiceOptions,
54-
req: NextApiRequest,
55-
pk: string,
56-
) => {
57-
switch (type) {
58-
case 'find':
59-
return options.find!(req.query)
60-
case 'create':
61-
return options.create!(req.body)
62-
case 'get':
63-
return options.get!(pk, req.query)
64-
case 'update':
65-
return options.update!(pk, req.body, req.query)
66-
case 'patch':
67-
return options.patch!(pk, req.body, req.query)
68-
case 'remove':
69-
return options.remove!(pk)
70-
}
71-
}
72-
73-
const handleService = async (
74-
type: keyof ServiceMethods,
75-
options: ServiceOptions,
42+
export const createService = (options: ServiceOptions) => async (
7643
req: NextApiRequest,
7744
res: NextApiResponse,
7845
) => {
79-
const pk = getPk(options.pk, req.query)
46+
logger(req, res, () => {})
8047

81-
const runHooks = async (hooks: Hook[]) => {
82-
for (let index = 0; index < hooks.length; index++) {
83-
await hooks[index](req, res, () => {})
48+
try {
49+
const pk = getPk(options.pk, req.query)
50+
const method = req.method?.toUpperCase()
51+
52+
const runHooks = async (hooks: Hook[]) => {
53+
for (let index = 0; index < hooks.length; index++) {
54+
await hooks[index](req, res, () => {})
55+
}
8456
}
85-
}
8657

87-
const allBeforeHooks = options.hooks?.before?.all ?? []
88-
const beforeHooks = options.hooks?.before?.[type] ?? []
89-
await runHooks([...allBeforeHooks, ...beforeHooks])
58+
switch (true) {
59+
case 'get' in options && pk && method === Method.GET: {
60+
const allBeforeHooks = options.hooks?.before?.all ?? []
61+
const beforeHooks = options.hooks?.before?.get ?? []
62+
await runHooks([...allBeforeHooks, ...beforeHooks])
9063

91-
const result = await runHandler(type, options, req, pk!)
64+
const result = await options.get!(pk!, req.query)
9265

93-
const allAfterHooks = options.hooks?.after?.all ?? []
94-
const afterHooks = options.hooks?.after?.[type] ?? []
95-
await runHooks([...allAfterHooks, ...afterHooks])
66+
const allAfterHooks = options.hooks?.after?.all ?? []
67+
const afterHooks = options.hooks?.after?.get ?? []
68+
await runHooks([...allAfterHooks, ...afterHooks])
9669

97-
const statusCode = getStatusCode(type)
70+
return res.status(Status.HTTP_200_OK).json(result)
71+
}
9872

99-
return res.status(statusCode).json(result)
100-
}
73+
case 'update' in options && pk && method === Method.PUT: {
74+
const allBeforeHooks = options.hooks?.before?.all ?? []
75+
const beforeHooks = options.hooks?.before?.update ?? []
76+
await runHooks([...allBeforeHooks, ...beforeHooks])
10177

102-
export const createService = (options: ServiceOptions) => async (
103-
req: NextApiRequest,
104-
res: NextApiResponse,
105-
) => {
106-
logger(req, res, () => {})
78+
const result = await options.update!(pk!, req.body, req.query)
10779

108-
try {
109-
const pk = getPk(options.pk, req.query)
110-
const { method } = req
80+
const allAfterHooks = options.hooks?.after?.all ?? []
81+
const afterHooks = options.hooks?.after?.update ?? []
82+
await runHooks([...allAfterHooks, ...afterHooks])
11183

112-
switch (true) {
113-
case options.get && pk && method === Method.GET:
114-
return handleService('get', options, req, res)
84+
return res.status(Status.HTTP_200_OK).json(result)
85+
}
86+
87+
case 'patch' in options && pk && method === Method.PATCH: {
88+
const allBeforeHooks = options.hooks?.before?.all ?? []
89+
const beforeHooks = options.hooks?.before?.patch ?? []
90+
await runHooks([...allBeforeHooks, ...beforeHooks])
91+
92+
const result = await options.patch!(pk!, req.body, req.query)
93+
94+
const allAfterHooks = options.hooks?.after?.all ?? []
95+
const afterHooks = options.hooks?.after?.patch ?? []
96+
await runHooks([...allAfterHooks, ...afterHooks])
97+
98+
return res.status(Status.HTTP_200_OK).json(result)
99+
}
100+
101+
case 'remove' in options && pk && method === Method.DELETE: {
102+
const allBeforeHooks = options.hooks?.before?.all ?? []
103+
const beforeHooks = options.hooks?.before?.remove ?? []
104+
await runHooks([...allBeforeHooks, ...beforeHooks])
105+
106+
const result = await options.remove!(pk!)
107+
108+
const allAfterHooks = options.hooks?.after?.all ?? []
109+
const afterHooks = options.hooks?.after?.remove ?? []
110+
await runHooks([...allAfterHooks, ...afterHooks])
111+
112+
return res.status(Status.HTTP_200_OK).json(result)
113+
}
114+
115+
case 'find' in options && !pk && method === Method.GET: {
116+
const allBeforeHooks = options.hooks?.before?.all ?? []
117+
const beforeHooks = options.hooks?.before?.find ?? []
118+
await runHooks([...allBeforeHooks, ...beforeHooks])
119+
120+
const result = await options.find!(req.query)
121+
122+
const allAfterHooks = options.hooks?.after?.all ?? []
123+
const afterHooks = options.hooks?.after?.find ?? []
124+
await runHooks([...allAfterHooks, ...afterHooks])
115125

116-
case options.update && pk && method === Method.PUT:
117-
return handleService('update', options, req, res)
126+
return res.status(Status.HTTP_200_OK).json(result)
127+
}
118128

119-
case options.patch && pk && method === Method.PATCH:
120-
return handleService('patch', options, req, res)
129+
case 'create' in options && !pk && method === Method.POST: {
130+
const allBeforeHooks = options.hooks?.before?.all ?? []
131+
const beforeHooks = options.hooks?.before?.create ?? []
132+
await runHooks([...allBeforeHooks, ...beforeHooks])
121133

122-
case options.remove && pk && method === Method.DELETE:
123-
return handleService('remove', options, req, res)
134+
const result = await options.create!(req.body)
124135

125-
case options.find && !pk && method === Method.GET:
126-
return handleService('find', options, req, res)
136+
const allAfterHooks = options.hooks?.after?.all ?? []
137+
const afterHooks = options.hooks?.after?.create ?? []
138+
await runHooks([...allAfterHooks, ...afterHooks])
127139

128-
case options.create && !pk && method === Method.POST:
129-
return handleService('create', options, req, res)
140+
return res.status(Status.HTTP_201_CREATED).json(result)
141+
}
130142

131143
default:
132144
throw new NotFoundError()

src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ export type ApiNextQuery = NextApiRequest['query']
99
export type ApiNextBody = NextApiRequest['body']
1010

1111
export interface ServiceMethods {
12-
find?(query: ApiNextQuery): Promise<any>
13-
create?: (body: ApiNextBody) => Promise<any>
1412
get?: (pk: string, query: ApiNextQuery) => Promise<any>
1513
update?: (pk: string, body: ApiNextBody, query: ApiNextQuery) => Promise<any>
1614
patch?: (pk: string, body: ApiNextBody, query: ApiNextQuery) => Promise<any>
1715
remove?: (pk: string) => Promise<any>
16+
find?(query: ApiNextQuery): Promise<any>
17+
create?: (body: ApiNextBody) => Promise<any>
1818
}
1919

2020
export type Hook = (

test/errors.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Method, Status } from 'simple-http-status'
2+
3+
import { createService, testService, NotAuthorised } from '../src'
4+
5+
describe('[errors/generic]', () => {
6+
test('Should return error object', async () => {
7+
const service = createService({
8+
hooks: {
9+
before: {
10+
get: [
11+
async () => {
12+
throw new NotAuthorised()
13+
},
14+
],
15+
},
16+
},
17+
get: async () => ({
18+
hello: 'there',
19+
}),
20+
})
21+
const { statusCode, data } = await testService(service, {
22+
method: Method.GET,
23+
query: { id: '1' },
24+
})
25+
expect(statusCode).toBe(Status.HTTP_401_UNAUTHORIZED)
26+
expect(data).toEqual({ errors: [{ message: 'Not authorised' }] })
27+
})
28+
})

0 commit comments

Comments
 (0)