apollo-fetch
Advanced tools
Comparing version 0.2.0 to 0.3.0
@@ -5,2 +5,7 @@ # Change log | ||
### 0.3.0 | ||
Changes the middleware and afterware to accept functions instead of objects | ||
`use` and `useAfter` now only accept a single middleware or afterware | ||
### 0.2.0 | ||
@@ -7,0 +12,0 @@ |
@@ -27,3 +27,3 @@ "use strict"; | ||
if (f) { | ||
f.applyMiddleware.apply(scope, [{ request: request, options: options }, next]); | ||
f.apply(scope, [{ request: request, options: options }, next]); | ||
} | ||
@@ -52,3 +52,3 @@ } | ||
if (f) { | ||
f.applyAfterware.apply(scope, [responseObject, next]); | ||
f.apply(scope, [responseObject, next]); | ||
} | ||
@@ -86,3 +86,2 @@ } | ||
httpError.response = response; | ||
httpError.raw = response.raw; | ||
httpError.parseError = error; | ||
@@ -106,3 +105,3 @@ throw httpError; | ||
parseError = e; | ||
return __assign({}, response, { raw: raw, parsed: null }); | ||
return __assign({}, response, { raw: raw }); | ||
} | ||
@@ -124,26 +123,20 @@ }); }) | ||
}, { | ||
use: function (middlewares) { | ||
middlewares.map(function (middleware) { | ||
if (typeof middleware.applyMiddleware === 'function') { | ||
_middlewares.push(middleware); | ||
} | ||
else { | ||
throw new Error('Middleware must implement the applyMiddleware function'); | ||
} | ||
}); | ||
use: function (middleware) { | ||
if (typeof middleware === 'function') { | ||
_middlewares.push(middleware); | ||
} | ||
else { | ||
throw new Error('Middleware must be a function'); | ||
} | ||
return apolloFetch; | ||
}, | ||
useAfter: function (afterwares) { | ||
afterwares.map(function (afterware) { | ||
if (typeof afterware.applyAfterware === 'function') { | ||
_afterwares.push(afterware); | ||
} | ||
else { | ||
throw new Error('Afterware must implement the applyAfterware function'); | ||
} | ||
}); | ||
useAfter: function (afterware) { | ||
if (typeof afterware === 'function') { | ||
_afterwares.push(afterware); | ||
} | ||
else { | ||
throw new Error('Afterware must be a function'); | ||
} | ||
return apolloFetch; | ||
}, | ||
_middlewares: _middlewares, | ||
_afterwares: _afterwares, | ||
}); | ||
@@ -150,0 +143,0 @@ return apolloFetch; |
export interface ApolloFetch { | ||
(operation: GraphQLRequest): Promise<FetchResult>; | ||
use: (middlewares: MiddlewareInterface[]) => ApolloFetch; | ||
useAfter: (afterwares: AfterwareInterface[]) => ApolloFetch; | ||
use: (middlewares: MiddlewareInterface) => ApolloFetch; | ||
useAfter: (afterwares: AfterwareInterface) => ApolloFetch; | ||
} | ||
@@ -10,3 +10,2 @@ export interface GraphQLRequest { | ||
operationName?: string; | ||
context?: object; | ||
} | ||
@@ -17,10 +16,4 @@ export interface FetchResult { | ||
extensions?: any; | ||
context?: object; | ||
} | ||
export interface AfterwareInterface { | ||
applyAfterware(response: ResponseAndOptions, next: Function): any; | ||
} | ||
export interface MiddlewareInterface { | ||
applyMiddleware(request: RequestAndOptions, next: Function): void; | ||
} | ||
export declare type MiddlewareInterface = (request: RequestAndOptions, next: Function) => void; | ||
export interface RequestAndOptions { | ||
@@ -30,2 +23,7 @@ request: GraphQLRequest; | ||
} | ||
export declare type AfterwareInterface = (response: ResponseAndOptions, next: Function) => void; | ||
export interface ResponseAndOptions { | ||
response: ParsedResponse; | ||
options: RequestInit; | ||
} | ||
export interface ParsedResponse extends Response { | ||
@@ -35,6 +33,2 @@ raw: string; | ||
} | ||
export interface ResponseAndOptions { | ||
response: ParsedResponse; | ||
options: RequestInit; | ||
} | ||
export interface FetchOptions { | ||
@@ -44,1 +38,5 @@ uri?: string; | ||
} | ||
export interface FetchError extends Error { | ||
response: ParsedResponse; | ||
parseError?: Error; | ||
} |
{ | ||
"name": "apollo-fetch", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "Lightweight implementation of fetch for GraphQL requests", | ||
"author": "Evans Hauser <evanshauser@gmail.com>", | ||
"contributors": [ | ||
"Jonas Helfer <jonas@helfer.email>" | ||
"Jonas Helfer <jonas@helfer.email>", | ||
"Sashko Stubailo <sashko@stubailo.com>" | ||
], | ||
@@ -9,0 +10,0 @@ "license": "MIT", |
271
README.md
@@ -1,3 +0,4 @@ | ||
# apollo-fetch | ||
# apollo-fetch [![npm version](https://badge.fury.io/js/apollo-fetch.svg)](https://badge.fury.io/js/apollo-fetch) [![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](http://www.apollostack.com/#slack) | ||
`apollo-fetch` is a lightweight client for GraphQL requests that supports middleware and afterware that modify requests and responses. | ||
@@ -7,16 +8,89 @@ | ||
# Installation | ||
``` | ||
npm install apollo-fetch --save | ||
``` | ||
To use `apollo-fetch` in a web browser or mobile app, you'll need a build system capable of loading NPM packages on the client. | ||
Some common choices include Browserify, Webpack, and Meteor +1.3. | ||
# Usage | ||
Simple GraphQL query: | ||
To create a fetch function capable of supporting middleware and afterware, use `createApolloFetch`: | ||
```js | ||
import { createApolloFetch } from 'apollo-fetch' | ||
import { createApolloFetch } from 'apollo-fetch'; | ||
const uri = 'http://api.githunt.com/graphql'; | ||
const apolloFetch = createApolloFetch({ uri }); | ||
``` | ||
To execute the fetch function, call `apolloFetch` directly in the following way: | ||
```js | ||
apolloFetch({ query, variables, operationName }) //all apolloFetch arguments are optional | ||
.then(result => { | ||
const { data, error, extensions } = result; | ||
//GraphQL errors and extensions are optional | ||
}) | ||
.catch(error => { | ||
//respond to a network error | ||
}); | ||
``` | ||
Middleware and Afterware are added with `use` and `useAfter` directly to `apolloFetch`: | ||
```js | ||
const apolloFetch = createApolloFetch(); | ||
const middleware = ({ request, options }, next) => { ... next(); }; | ||
const afterware = ({ response, options }, next) => { ... next(); }; | ||
apolloFetch.use(middleware); | ||
apolloFetch.useAfter(afterware); | ||
``` | ||
Middleware and Afterware can be chained together in any order: | ||
```js | ||
const apolloFetch = createApolloFetch(); | ||
apolloFetch | ||
.use(middleware1) | ||
.use(middleware2) | ||
.useAfter(afterware1) | ||
.useAfter(afterware2) | ||
.use(middleware3); | ||
``` | ||
The `apolloFetch` from `apollo-fetch` is an alias for an empty call to `createApolloFetch` | ||
```js | ||
import { apolloFetch } from `apollo-fetch`; | ||
//fetches a query from /graphql | ||
apolloFetch({ query }).then(...).catch(...); | ||
``` | ||
For mocking and other fetching behavior, you may pass a fetch into `createApolloFetch`: | ||
```js | ||
const customFetch = createFileFetch(); | ||
const apolloFetch = createApolloFetch({ customFetch }); | ||
``` | ||
# Examples | ||
### Simple GraphQL Query | ||
```js | ||
import { createApolloFetch } from 'apollo-fetch'; | ||
const uri = 'http://api.githunt.com/graphql'; | ||
const query = ` | ||
query sampleQuery(id: ID!) { | ||
sample(id: $id) { | ||
id, | ||
name | ||
query CurrentUser { | ||
currentUser { | ||
login, | ||
} | ||
@@ -27,17 +101,9 @@ } | ||
apolloFetch({ query }) | ||
.then( result => { | ||
// GraphQL data, GraphQL errors and GraphQL extensions | ||
const { data, error, extensions } = result; | ||
}) | ||
.catch(error => { | ||
//respond to a network error | ||
}) | ||
apolloFetch({ query }).then(...).catch(...); | ||
``` | ||
Simple GraphQL mutation with authentication middleware. | ||
Middleware has access to the GraphQL query and the options passed to fetch. | ||
### Simple GraphQL Mutation with Variables | ||
```js | ||
import { createApolloFetch } from 'apollo-fetch' | ||
import { createApolloFetch } from 'apollo-fetch'; | ||
@@ -47,33 +113,45 @@ const uri = 'http://api.githunt.com/graphql'; | ||
const query = ` | ||
query sampleMutation(id: ID!) { | ||
addSample(id: $id) { | ||
mutation SubmitRepo ($repoFullName: String!) { | ||
submitRepository (repoFullName: $repoFullName) { | ||
id, | ||
name | ||
score, | ||
} | ||
} | ||
` | ||
`; | ||
const variables = { | ||
repoFullName: 'apollographql/apollo-fetch', | ||
}; | ||
const apolloFetch = createApolloFetch({ uri }); | ||
apolloFetch.use([{ | ||
applyMiddleware: ({ request, options }, next) => { | ||
if (!options.headers) { | ||
options.headers = {}; // Create the headers object if needed. | ||
} | ||
options.headers['authorization'] = 'created token'; | ||
apolloFetch({ query, variables }).then(...).catch(...); | ||
``` | ||
next(); | ||
}, | ||
}]); | ||
### Middleware | ||
apolloFetch({ query }) | ||
.then(result => { | ||
// GraphQL data, errors, and extensions | ||
const { data, error, extensions } = result; | ||
}) | ||
.catch(error => { | ||
//respond to a network error | ||
}) | ||
A GraphQL mutation with authentication middleware. | ||
Middleware has access to the GraphQL query and the options passed to fetch. | ||
```js | ||
import { createApolloFetch } from 'apollo-fetch'; | ||
const uri = 'http://api.githunt.com/graphql'; | ||
const apolloFetch = createApolloFetch({ uri }); | ||
apolloFetch.use(({ request, options }, next) => { | ||
if (!options.headers) { | ||
options.headers = {}; // Create the headers object if needed. | ||
} | ||
options.headers['authorization'] = 'created token'; | ||
next(); | ||
}); | ||
apolloFetch(...).then(...).catch(...); | ||
``` | ||
### Afterware | ||
Afterware to check the response status and logout on a 401. | ||
@@ -83,3 +161,3 @@ The afterware has access to the raw reponse always and parsed response when the data is proper JSON. | ||
```js | ||
import { createApolloFetch } from 'apollo-fetch' | ||
import { createApolloFetch } from 'apollo-fetch'; | ||
@@ -90,39 +168,69 @@ const uri = 'http://api.githunt.com/graphql'; | ||
apolloFetch.useAfter([{ | ||
applyAfterware: ({ response }, next) => { | ||
if (response.status === 401) { | ||
logout(); | ||
} | ||
next(); | ||
}, | ||
}]); | ||
apolloFetch.useAfter(({ response }, next) => { | ||
if (response.status === 401) { | ||
logout(); | ||
} | ||
next(); | ||
}); | ||
apolloFetch({ query }) | ||
.then(result => { | ||
// GraphQL data, errors, and extensions from the server | ||
const { data, error, extensions } = result; | ||
}) | ||
.catch(error => { | ||
//respond to a network error | ||
}) | ||
apolloFetch(...).then(...).catch(...); | ||
``` | ||
Middleware and Afterware can be chained together in any order: | ||
### Error Handling | ||
All responses are passed to the afterware regardless of the http status code. | ||
Network errors, `FetchError`, are thrown after the afterware is run and if no parsed response is received. | ||
This example shows an afterware that can receive a 401 with an unparsable response and return a valid `FetchResult`. | ||
Currently all other status codes that have an uparsable response would throw an error. | ||
This means if a server returns a parsable GraphQL result on a 403 for example, the result would be passed to `then` without error. | ||
Errors in Middleware and Afterware are propagated without modification. | ||
```js | ||
const apolloFetch = createApolloFetch(); | ||
apolloFetch.use([exampleWare1]) | ||
.use([exampleWare2]) | ||
.useAfter([exampleWare3]) | ||
.useAfter([exampleWare4]) | ||
.use([exampleWare5]); | ||
import { createApolloFetch } from 'apollo-fetch'; | ||
const uri = 'http://api.githunt.com/graphql'; | ||
const apolloFetch = createApolloFetch({ uri }); | ||
apolloFetch.useAfter(({ response }, next) => { | ||
//response.raw will be a non-null string | ||
//response.parsed may be a FetchResult or undefined | ||
if (response.status === 401 && !response.parsed) { | ||
//set parsed response to valid FetchResult | ||
response.parsed = { | ||
data: { user: null }, | ||
}; | ||
} | ||
next(); | ||
}); | ||
//Here catch() receives all responses with unparsable data | ||
apolloFetch(...).then(...).catch(...); | ||
``` | ||
`apolloFetch` is an alias for an empty call to `createApolloFetch` | ||
### Apollo Integration | ||
`apollo-fetch` is the first part of [Apollo Client's](https://github.com/apollographql/apollo-client) future network stack. | ||
If you would like to try it out today, | ||
you may replace the network interface with the following: | ||
```js | ||
import { apolloFetch } from `apollo-fetch`; | ||
import ApolloClient from 'apollo-client'; | ||
import { createApolloFetch } from 'apollo-fetch'; | ||
import { print } from 'graphql/language/printer'; | ||
//fetches a query from /graphql | ||
apolloFetch({ query }).then(...).catch(...) | ||
const uri = 'http://api.githunt.com/graphql'; | ||
const apolloFetch = createApolloFetch({ uri }); | ||
const networkInterface = { | ||
request: (req) => apolloFetch({...req, query: print(req.query)}) | ||
} | ||
const client = new ApolloClient({ | ||
networkInterface, | ||
}); | ||
``` | ||
@@ -142,3 +250,4 @@ | ||
} | ||
/* defaults: | ||
/* | ||
* defaults: | ||
* uri = '/graphql' | ||
@@ -154,8 +263,9 @@ * customFetch = fetch from isomorphic-fetch | ||
(operation: GraphQLRequest): Promise<FetchResult>; | ||
use: (middlewares: MiddlewareInterface[]) => ApolloFetch; | ||
useAfter: (afterwares: AfterwareInterface[]) => ApolloFetch; | ||
use: (middlewares: MiddlewareInterface) => ApolloFetch; | ||
useAfter: (afterwares: AfterwareInterface) => ApolloFetch; | ||
} | ||
``` | ||
`GraphQLRequest` is the argument to an `ApolloFetch` call | ||
`GraphQLRequest` is the argument to an `ApolloFetch` call. | ||
`query` is optional to support persistent queries based on only an `operationName`. | ||
@@ -167,3 +277,2 @@ ```js | ||
operationName?: string; | ||
context?: object; | ||
} | ||
@@ -179,3 +288,2 @@ ``` | ||
extensions?: any; | ||
context?: object; | ||
} | ||
@@ -187,5 +295,3 @@ ``` | ||
```js | ||
MiddlewareInterface { | ||
applyMiddleware(request: RequestAndOptions, next: Function): void; | ||
} | ||
MiddlewareInterface: (request: RequestAndOptions, next: Function) => void | ||
@@ -201,5 +307,3 @@ RequestAndOptions { | ||
```js | ||
AfterwareInterface { | ||
applyAfterware(response: ResponseAndOptions, next: Function): any; | ||
} | ||
AfterwareInterface: (response: ResponseAndOptions, next: Function) => void | ||
@@ -212,3 +316,3 @@ ResponseAndOptions { | ||
`ParsedResponse` adds `raw` (the body from the `.text()` call) to the fetch result, and `parsed` (the parsed JSON from `raw`) to the regular Response from the fetch call. | ||
`ParsedResponse` adds `raw` (the body from the `.text()` call) to the fetch result, and `parsed` (the parsed JSON from `raw`) to the fetch's standard [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). | ||
@@ -222,3 +326,6 @@ ```js | ||
Errors returned from a call to `ApolloFetch` are normal errors that contain the parsed response, the raw response from .text(), and a possible parse error. | ||
A `FetchError` is returned from a failed call to `ApolloFetch` | ||
is standard [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) that contains the response and a possible parse error. | ||
The `parseError` is generated when the raw response is not valid JSON (when `JSON.parse()` throws) and the Afterware does not add an object to the response's `parsed` property. | ||
Errors in Middleware and Afterware are propagated without modification. | ||
@@ -228,4 +335,4 @@ ```js | ||
response: ParsedResponse; | ||
raw: string; | ||
parseError?: Error; | ||
} | ||
``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
37033
325
277