@pryum/synapse
v1.1.1-beta4
Published
An http client that leverages ES6 decorators to design clean and versatile HTTP api
Downloads
14
Maintainers
Readme
Synapse
Leverages ES6 decorators to design clean and versatile HTTP api, the easy way. Promises, observables and Fetch API all included !
Disclaimer
This project is still in beta
- Feedback needed
- Angular AOT not confirmed to work
- No "known bugs" list
What is it ?
We all already did, one day, some straight forward http calls, concatenating url parts, parameters...
const BASE_URL='https://my-awesome-web-api-PROD'
class UserApi {
constructor(private http: HttpClient) {}
getUser(userId: number): Observable { return this.http.get(BASE_URL + '/users/' + userId); }
getAccount(userId: number): Observable {
return this.http.get(BASE_URL + '/users/' + userId + '/account/', {
headers: {
authorisation: '$$mySecretAuthToken$$'
}
});
}
searchUser(searchParameters): Observable {
return this.http.get(BASE_URL + '/users?name=' + searchParameters.name
+ (searchParameters.age ? '&age=' + searchParameters.age : '')
+ (searchParameters.city ? 'city=' + searchParameters.city : ''));
}
}
Don't let your web api be that fuzzy anymore. Synapse is a library that allows you to achieve the same as above with much less boilerplate code:
@SynapseApi({
baseUrl: 'https://my-awesome-web-api-PROD',
path: 'users'
})
class UserApi {
@GET(':id')
getUser(@PathParams() userId: number) { return Synapse.OBSERVABLE; }
@GET({
url: ':id/account',
requestHandlers: [myAuthRequestHandler]
})
getAccount(@PathParams() userId: number) { return Synapse.OBSERVABLE; }
@GET('users')
searchUser(@QueryParams() searchParameters) { return Synapse.OBSERVABLE; }
}
Installation
via npm
npm install --save @pryum/Synapse
via yarn
yarn add @pryum/Synapse
Please note that the project comes with the following polyfills:
- reflect-metadata for ES7 Metadata Reflection API
- whatwg-fetch for ES6 Fetch API
- url-search-params-polyfill for ES6 UrlSearchParams
Setup Synapse with angular.
Synapse has only been tested with Angular (>= 5)
Synapse comes with an HttpBackendAdapter
that makes use of built in angular's HttpClient
.
As a result, be sure to import BrowserModule
and HttpClientModule
before importing SynapseModule
.
To be able to use Synapse, your main module (usually AppModule
) should look like the following:
import { SynapseModule } from '@pryum/synapse';
@NgModule({
// ...
imports: [
BrowserModule,
HttpClientModule,
SynapseModule.forRoot({
baseUrl : 'https://your-awesome-api'
})
]
// ...
})
export class AppModule {}
Usage with Angular <4 at your own risk. You will need to implement your own
HttpBackendAdapter
based on angular'sHttp
Setup Synapse without angular.
Although it should work seamlessly, usage with plain javascript has not been tested yet.
At the moment, Synapse as only been tested with Angular, but it has been designed to be framework agnostic.
Working with any other framework should be fairly easy, and all you probably need to do is to provide your own HttpBackendAdapter
.
HttpBackendAdapter
itself relies on the standard Fetch API :
import { HttpBackendAdapter } from '@pryum/synapse';
class CustomHttpAdapter implements HttpBackendAdapter {
get(request: Request): Promise<Response>;
post(request: Request): Promise<Response>;
put(request: Request): Promise<Response>;
patch(request: Request): Promise<Response>;
delete(request: Request): Promise<Response>;
}
const yourHttpAdapter = new CustomHttpAdapter();
Synapse.init({ httpBackend : yourHttpAdapter, baseUrl : 'https://your-awesome-api' });
A simple Synapse API example.
Synapse is annotation driven (well, decorator driven). All you need to do is to put proper annotations on proper classes, methods and parameters, with proper configuration.
/**
* Annotate any class with @SynapseApi to turn it into a web api.
*/
@SynapseApi('users')
// @Injectable() If you use angular, you might want to add this.
export class UsersApi {
/**
* ex : new UsersApi().getPage({name: 'bob'}) => GET https://your-awesome-api/users?name=bob
*/
@GET()
getPage(@QueryParams() params?: Object): Observable<User[]> {
return Synapse.OBSERVABLE; // this is some dummy value, just to stop Typescript compiler from complaining about bad return type.
}
/**
* ex : new UsersApi().getOne(12) => GET https://your-awesome-api/users/12
*/
@GET(':id')
getOne(@PathParam() id: number): Promise<User> {
return Synapse.PROMISE; // saw that ? Synapse also support returning promises.
}
/**
* ex : new UsersApi().postOne(new User()) => POST https://your-awesome-api/users
*/
@POST()
postOne(@Body() user: User): Observable<User> {
return Synapse.OBSERVABLE;
}
/**
* ex : new UsersApi().putOne(new User(), {'x-some-headers': 'some value'}) => POST https://your-awesome-api/users
*/
@PUT({
mapper: (userObject) => new User(userObject) // I want Synapse to automatically map the response to another object.
})
putOne(@Body({
mapper: (user) => { return {id: user.id, name: user.name}} // This call will only let these two attributes reach the server
}) user: User, @Headers() headers?: any): Observable<User> {
return Synapse.OBSERVABLE;
}
}
As you can see above, no boilerplate code is needed. To issue http request to the server, just naturally call your api like a regular javascript function:
Method bodies should be empty. If a method has a body, it will be ignored at runtime.
Synapse method can return either a Promise or an Observable. Return type is inferred from the value your function returns, so be careful to return proper
Synapse.OBSERVABLE
orSynapse.PROMISE
(any other observable / promise would work as well)
Advanced usage
Synapse owns a global configuration of type SynapseConf
, that is set through the call to SynapseModule.forRoot(/* ... */)
(or Synapse.init(/* ... */)
if you don't use angular).
SynapseConfig:
baseUrl
- type :
string
- default value :
null
- description : the base url of your api.
- type :
requestHandlers
- type :
HttpRequestHandler[]
- default value :
[]
- description : these hooks are called before each request to the api
- type :
responseHandlers
- type :
HttpResponseHandler[]
- default value :
[]
- description : these hooks are called after each response from the api
- type :
observe
- type :
ObserveType
- default value :
ObserveType.BODY
description : Observe only the body by default. That is, any method of the API will return apromise
(or anObservable
) that holds the (eventually mapped) response's body only.
- type :
The same way ObserveType.BODY returns the mapped response body, ObserveType.RESPONSE returns the complete response with the mapped body. Because [ES6's Response] as only a limited set of possible body types, ObserveType.RESPONSE returns in fact an object of type TypedResponse, that acts as a Response but with a body that holds a complete (non asynchronous) body of any type.
headers
- type :
Object
- default value :
{}
- description : Any global header you want to apply tho all the requests
- type :
httpBackend
- type :
HttpBackendAdapter
- default value :
HttpClient
(angular) or elseFetch API
- description : The Http backend implementation to use to make http requests. You can submit your own adapter.
- type :
contentType
- type :
ContentTypeConstants
- default value :
ContentTypeConstants.JSON
- description : Default content type expected to be returned by methods of the API. If an endpoint's Content-Type doesn't return JSON, it will throw an error unless you explicitly configure the proper Content-Type
- type :
This configuration is passed down to each of your classes decorated with @SynapseApi
,
and to their respective methods decorated with @GET
, @POST
, @PUT
, @PATCH
or @DELETE
, which in turn can override it.
SynapseApiConfig:
This is the configuration provided to a class decorated with @SynapseApi
. It mostly inherits from SynapseConfig
,
except it can configure a path
that comes in addition to the baseUrl
.
path
- type :
string
- default value :
''
- description : The
path
is concatenated with thebaseUrl
(either global or overridden), to produce a full URLof formhttp://base-url/path
- type :
mapper
: MapperType<any, any>;- type :
MapperType
- default value :
null
- description : A function that is called when method respond, to map the result to another type. This function receive the response's body in argument, and return the mapped object.
- type :
SynapseApiConfig:
This is the configuration provided to methods decorated with @GET
, @POST
, @PUT
, @PATCH
or @DELETE
. It is mostly inherits from SynapseApiConfig
except it does not allow overriding the baseUrl
.
path
- type :
string
- default value :
''
- description : The
path
is concatenated with thebaseUrl
and thepath
defined by a parent, to produce a full URLof formhttp://base-url/api-path/endpoint-path
- type :
Both
@SynapseApi
and@GET,
@POST,
etc can configure propertiesrequestHandlers
,responseHandlers
,headers
. Providing those configurations will not override existing one but rather merge with it.
FAQ [Needs updates]
I get Property 'defineMetadata' does not exist on type 'typeof Reflect'
Did you installed reflect-metadata polyfill ? If you are using @angular/cli, please add
import 'reflect-metadata';
to the file
polyfills.ts
Error: Synapse not initialized
Did you properly called
SynapseModule.forRoot(/* ... */)
in your angular's AppModule, orSynapse.init(/* ... */)
?
Known bugs! [Needs updates]
- nothing so far
Want to contribute?
Thank you dherges
dherges/ng-packagr was used as a blueprint for the structure of this project.