Giter VIP home page Giter VIP logo

graphman's Introduction

GraphMan


Quickly scaffold a postman collection for a GraphQL API.


GraphMan CLI generates an complete collection from a GraphQL endpoint, containing one request per query & mutation, with pre filled fields, parameters and variables.

Note: GraphMan is designed for the postman-collection spec 2.1

✨GraphMan is fully compatible with the Insomnia API Client out of the box!✨

Status

Version CD Checks

Motivation

Visualizing and exploring existing graphql APIs can be quite daunting. Using postman to manage all your apis is pretty standard, however creating and maintaining collections is difficult. GraphMan makes thoses things easy, helping you for:

  • Graph discovery
  • Graph testing
  • Collection updating

Usage

Run from url

GraphMan uses deno as a javascript / typescript runtime. That allows to run the CLI from the file url. To get started:

  1. Install deno
  2. Run: deno run https://deno.land/x/[email protected]/src/cli.ts <graphql endpoint url>
  3. Import the generated [...].postman_collection.json file in postman.

Install GraphMan

If you want to access graphman easly you can "install" it on your machine:

  1. Install deno
  2. Run: deno install -r -f --allow-net --allow-write -n graphman https://deno.land/x/[email protected]/src/cli.ts
  3. The command will output export PATH="..." copy paste it in your ~/.bashrc or ~/.zshrc file to add graphman to your path. You can now run graphman using the graphman <params> command! 🎉 To update GraphMan just reproduce the step 2.

Note: this is not a real installation, it just creates a script that basically aliases the run form url command.

Run locally

  1. Clone the repo
  2. Run deno run src/index.ts <params>

Note that deno will ask for network and file-system permissions as it's runtime is secure by default

The relases are mirrored at https://deno.land/x/graphman@VERSION, you can use previous versions if needed.

CLI Options

  • Custom output filename: --out=FILNAME
  • Headers: -H="header:value", can be used multiple times.
  • Get help: --help or -h

Examples

You can try graphman on public graphql APIs, and it is a great way to get started with graphQL:

  • Rick&Morty API: deno run https://deno.land/x/[email protected]/src/cli.ts https://rickandmortyapi.com/graphql
GraphMan collection for the Rick and morty API
Character query for the Rick and morty API collection

Issues and contributions

  • Please open an issue if you encounter bugs, with reproduction steps and error message.
  • For feature requests, please open an issue too
  • Feel free to create merge requests to improve GraphMan !

graphman's People

Contributors

lucerowb avatar nohehf avatar notrab avatar nullswan avatar silval4-ppb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphman's Issues

feat: collection to queries

Add the "reverse" feature. After completing your collection in your API client, you export it and generate a query file out of it.

feat: improve error message when the request fails

Hello, graphman worked with my local backend, nice <3

I tried to do that

# create a new token
$ open https://github.com/settings/tokens/new
$ export GITHUB_TOKEN="xxxxxxxx"
# works for me
$ curl -H "Authorization: bearer $GITHUB_TOKEN" https://api.github.com/graphql
$ alias graphman="deno  run  https://deno.land/x/[email protected]/src/index.ts"
$ graphman https://api.github.com/graphql --auth="bearer $GTHUB_TOKEN"
Creating the postman collection for https://api.github.com/graphql
⚠️  ️Deno requests net access to "api.github.com". Run again with --allow-net to bypass this prompt.
   Allow? [y/n (y = yes allow, n = no deny)]  y
error: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '__schema')
  const queryTypeName = introspectionQuery.__schema.queryType
                                           ^
    at createPostmanCollection (https://deno.land/x/[email protected]/src/lib.ts:308:44)
    at async https://deno.land/x/[email protected]/src/index.ts:38:20

Reference: https://docs.github.com/en/graphql/guides/forming-calls-with-graphql#communicating-with-graphql

EDIT:

The actual issue is the lack of a good error message when the http call fails

As it turns out it was a typo (thanks for the comments)

$ graphman https://api.github.com/graphql --auth="bearer $GITHUB_TOKEN"
Collection saved at ./out/api.github.com-GraphMan.postman_collection.json
Import it in postman and complete the queries ! 🚀

Feat: pass options as flags

#12 and #9 features, and basically any future optional parameter, should be passed through flags.
For instance:

  • Changing file output should be done via: --output <filename> or -o <filename>
  • Passing a bearer token should be done via: --bearer <token>
    Just wondering if I'll write the parser from scratch or use one to ensure robustness.

Docker

Great tool 🔥🔥

Can you please dockerize it?

feat: support collection scoped variables & headers

GraphMan should be able to take auth and headers as parameters.
Those parameters as well as URLs should be sent in the environment variables.
The process could be to load .env files to generate different environment, or via the CLI.

Unions not yet supported?

Getting an error

error: Uncaught (in promise) Error: Kind UNION for type WorkItem is not supported yet. Please open an issue.

Is this a known limitation at the moment or maybe I have something configured incorrectly?

Import of collection to Postman ends with error "format not recognized"

When I import collection generated by Graphman v1.2.0 to Postman (version 10.8.0 and version 9.31.0) it fails with error message Error while importing: format not recognized

Beginning of generated collection:

{
	"postmanCollection": {
		"info": {
			"name": "rori-editor.backend.doeg8.praha.tmapy.cz-GraphMan",
			"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
		},
		"item": [
			{
				"name": "Queries",
				"item": [
					{
						"name": "mapApp",
						"request": {
							"method": "POST",
							"header": [],
							"body": {
								"mode": "graphql",
								"graphql": {
									"query": "query mapApp($id: GlobalID!) {\n  mapApp(id: $id) {\n    __typename\n    id\n    slug\n    title\n  }\n}",
									"variables": "{\n\t\"id\": null\n}"
								}
							},
...

Any hint how to fix it?

Out directory not found

error: Uncaught (in promise) NotFound: No such file or directory (os error 2), open 'out/rickandmortyapi.com-GraphMan.postman_collection.json'
  Deno.writeTextFileSync(path, JSON.stringify(json, null, "\t"));
       ^
    at Object.opSync (deno:core/01_core.js:172:12)
    at writeFileSync (deno:runtime/js/40_write_file.js:14:10)
    at Object.writeTextFileSync (deno:runtime/js/40_write_file.js:61:12)
    at saveJsonFormatted (https://deno.land/x/[email protected]/src/lib.ts:61:8)
    at https://deno.land/x/[email protected]/src/index.ts:40:1

Seperate Mutations and Queries into folders(item groups)

When a GraphQL schema is converted to a collection, the queries and mutation are all placed under the default collection folder. Taking a glance at each collection request, you'll find it really hard to know which is a mutation or a query. You'll have to first open up individual requests to find out.

Separating mutations and queries into their respective folders(item groups) helps solve this easily and gives them a designated home in the collection.

Interfaces are not supported

Hello, since it's asked in the error message and I didn't find an issue already referencing this - running graphman on my schema returns this error:

error: Uncaught (in promise) Error: Kind INTERFACE for type <TheInterfaceName> is not supported yet. Please open an issue.

Here is an exemple with a public schema:

deno run https://deno.land/x/[email protected]/src/cli.ts https://swapi-graphql.netlify.app/.netlify/functions/index

Error: Query type not found

I get this error when running the command:

error: Uncaught (in promise) Error: Query type not found
  if (!queryType) throw new Error("Query type not found");
                        ^
    at createPostmanCollection (https://raw.githubusercontent.com/Escape-Technologies/graphman/main/src/lib.ts:296:25)
    at async https://raw.githubusercontent.com/Escape-Technologies/graphman/main/src/index.ts:23:20

Maybe it is because the type of my query is called "MyAppQuery" instead of "Query"? (same thing for mutations).
This is allowed by the GraphQL spec.

In the introspection query this is indicated by this:

{
    "_queryType": "MyAppQuery",
    "_mutationType": "MyAppMutation"
    ...
}

feat: add unit & e2e tests

Tests

unit

  • TypeFormater
  • introspection to collection -> then check the validity of the queries with graphql.parse

e2e

  • Endpoint to collection
    • should use our own API?
    • can maybe parse it with postman and insomnia CLIs

feat: collection to queries

Introduce a mode to "reverse" graphman:

  • Export your API collection from your API Client (as postman format)
  • Run graphman reverse <path_to_collection> (temporary command name)
  • It generates a (.ts file with a list | json file) of graphql queries as strings

feat: Add variables defaults

Variables defaults will allow queries to be valid out of the box in most cases and will complete the full support of Insomnia.

feat: support other GraphQL clients by outputing to plain folders and .graphql files

Bonjour,

I heard from graphman at https://dev.to/tristankalos/graphman-generate-a-postman-collection-for-any-graphql-endpoint-4hff

Your tool seems like something that is totally needed.

GrapphQL being a standard, I feel like it should nothing in it is specific to Postman no ?

Apart from the very last step where you store the queries in the Postman collection format.

I use https://insomnia.rest/ and the GraphQL plugin for IntelliJ

If you have an option to write the queries simply in a plain folder with plain some-query.graphql files, I would totally use it.

Generate mock responses for mutations and queries.

GraphQL is a very declarative query language. It provides a declarative approach to data fetching. From the schema, you already know what kind of data to expect and you can choose to request just what you need.

We can leverage the declarative nature of GraphQL and generate mock response data that can be included in the generated collection. Having responses pre-generated in a collection means consumers of that collection can create mock servers for that API by just clicking a few buttons without having to worry about populating the collection with sample responses from scratch.

Example doesn't work

I wanted to test just the example deno run https://deno.land/x/[email protected]/src/index.ts https://rickandmortyapi.com/graphql before testing with my application and I immediately got the following error:

Check https://deno.land/x/[email protected]/src/index.ts
error: TS2322 [ERROR]: Type 'AliasName<K, A>' is not assignable to type 'string | number | symbol'.
  Type 'K | (string extends A[K] ? K : A[K] extends string ? K | A[K] : K)' is not assignable to type 'string | number | symbol'.
    Type 'string extends A[K] ? K : A[K] extends string ? K | A[K] : K' is not assignable to type 'string | number | symbol'.
      Type 'K | (A[K] extends string ? K | A[K] : K)' is not assignable to type 'string | number | symbol'.
        Type 'A[K] extends string ? K | A[K] : K' is not assignable to type 'string | number | symbol'.
          Type 'K | A[K]' is not assignable to type 'string | number | symbol'.
            Type 'A[K]' is not assignable to type 'string | number | symbol'.
              Type 'A[string] | A[number] | A[symbol]' is not assignable to type 'string | number | symbol'.
                Type 'A[K]' is not assignable to type 'symbol'.
                  Type 'A[string] | A[number] | A[symbol]' is not assignable to type 'symbol'.
                    Type 'A[string]' is not assignable to type 'symbol'.
                      Type 'A[K] extends string ? K | A[K] : K' is not assignable to type 'symbol'.
                        Type 'K | A[K]' is not assignable to type 'symbol'.
                          Type 'keyof A & K' is not assignable to type 'symbol'.
                            Type 'string extends A[K] ? K : A[K] extends string ? K | A[K] : K' is not assignable to type 'symbol'.
                              Type 'K | (A[K] extends string ? K | A[K] : K)' is not assignable to type 'symbol'.
                                Type 'keyof A & K' is not assignable to type 'symbol'.
                                  Type 'AliasName<keyof T, A>' is not assignable to type 'string | number | symbol'.
                                    Type 'keyof T | (string extends A[keyof A & keyof T] ? keyof A & keyof T : A[keyof A & keyof T] extends string ? (keyof A & keyof T) | A[keyof A & keyof T] : keyof A & keyof T)' is not assignable to type 'string | number | symbol'.
                                      Type 'string extends A[keyof A & keyof T] ? keyof A & keyof T : A[keyof A & keyof T] extends string ? (keyof A & keyof T) | A[keyof A & keyof T] : keyof A & keyof T' is not assignable to type 'string | number | symbol'.
                                        Type '(keyof A & keyof T) | (A[keyof A & keyof T] extends string ? (keyof A & keyof T) | A[keyof A & keyof T] : keyof A & keyof T)' is not assignable to type 'string | number | symbol'.
                                          Type 'A[keyof A & keyof T] extends string ? (keyof A & keyof T) | A[keyof A & keyof T] : keyof A & keyof T' is not assignable to type 'string | number | symbol'.
                                            Type '(keyof A & keyof T) | A[keyof A & keyof T]' is not assignable to type 'string | number | symbol'.
                                              Type 'A[keyof A & keyof T]' is not assignable to type 'string | number | symbol'.
                                                Type 'A[string] | A[number] | A[symbol]' is not assignable to type 'string | number | symbol'.
                                                  Type 'A[string]' is not assignable to type 'string | number | symbol'.
                                                    Type 'A[string]' is not assignable to type 'symbol'.
                                                      Type 'A[keyof A & keyof T]' is not assignable to type 'symbol'.
                                                        Type 'A[string] | A[number] | A[symbol]' is not assignable to type 'symbol'.
                                                          Type 'A[string]' is not assignable to type 'symbol'.
                                                            Type 'A[keyof A & keyof T] extends string ? (keyof A & keyof T) | A[keyof A & keyof T] : keyof A & keyof T' is not assignable to type 'symbol'.
                                                              Type '(keyof A & keyof T) | A[keyof A & keyof T]' is not assignable to type 'symbol'.
                                                                Type 'keyof A & keyof T' is not assignable to type 'symbol'.
                                                                  Type 'string extends A[keyof A & keyof T] ? keyof A & keyof T : A[keyof A & keyof T] extends string ? (keyof A & keyof T) | A[keyof A & keyof T] : keyof A & keyof T' is not assignable to type 'symbol'.
                                                                    Type '(keyof A & keyof T) | (A[keyof A & keyof T] extends string ? (keyof A & keyof T) | A[keyof A & keyof T] : keyof A & keyof T)' is not assignable to type 'symbol'.
                                                                      Type 'keyof A & keyof T' is not assignable to type 'symbol'.
                                                                        Type 'AliasName<string, A> | AliasName<number, A> | AliasName<symbol, A>' is not assignable to type 'string | number | symbol'.
                                                                          Type 'AliasName<string, A>' is not assignable to type 'string | number | symbol'.
                                                                            Type 'string | (string extends A[keyof A & string] ? keyof A & string : A[keyof A & string] extends string ? (keyof A & string) | A[keyof A & string] : keyof A & string)' is not assignable to type 'string | number | symbol'.
                                                                              Type 'string extends A[keyof A & string] ? keyof A & string : A[keyof A & string] extends string ? (keyof A & string) | A[keyof A & string] : keyof A & string' is not assignable to type 'string | number | symbol'.
                                                                                Type '(keyof A & string) | (A[keyof A & string] extends string ? (keyof A & string) | A[keyof A & string] : keyof A & string)' is not assignable to type 'string | number | symbol'.
                                                                                  Type 'A[keyof A & string] extends string ? (keyof A & string) | A[keyof A & string] : keyof A & string' is not assignable to type 'string | number | symbol'.
                                                                                    Type '(keyof A & string) | A[keyof A & string]' is not assignable to type 'string | number | symbol'.
                                                                                      Type 'A[keyof A & string]' is not assignable to type 'string | number | symbol'.
                                                                                        Type 'A[string]' is not assignable to type 'string | number | symbol'.
                                                                                          Type 'A[string]' is not assignable to type 'symbol'.
                                                                                            Type 'A[keyof A & string]' is not assignable to type 'symbol'.
                                                                                              Type 'A[string]' is not assignable to type 'symbol'.
                                                                                                Type 'A[keyof A & string] extends string ? (keyof A & string) | A[keyof A & string] : keyof A & string' is not assignable to type 'symbol'.
                                                                                                  Type '(keyof A & string) | A[keyof A & string]' is not assignable to type 'symbol'.
                                                                                                    Type 'keyof A & string' is not assignable to type 'symbol'.
                                                                                                      Type 'string extends A[keyof A & string] ? keyof A & string : A[keyof A & string] extends string ? (keyof A & string) | A[keyof A & string] : keyof A & string' is not assignable to type 'symbol'.
                                                                                                        Type '(keyof A & string) | (A[keyof A & string] extends string ? (keyof A & string) | A[keyof A & string] : keyof A & string)' is not assignable to type 'symbol'.
                                                                                                          Type 'keyof A & string' is not assignable to type 'symbol'.
                                                                                                            Type 'AliasName<string, A>' is not assignable to type 'symbol'.
                                                                                                              Type 'AliasName<keyof T, A>' is not assignable to type 'symbol'.
                                                                                                                Type 'AliasName<K, A>' is not assignable to type 'symbol'.
                                                                                                                  Type 'K | (string extends A[K] ? K : A[K] extends string ? K | A[K] : K)' is not assignable to type 'symbol'.
                                                                                                                    Type 'K' is not assignable to type 'symbol'.
                                                                                                                      Type 'keyof T' is not assignable to type 'symbol'.
                                                                                                                        Type 'AliasName<keyof T, A>' is not assignable to type 'symbol'.
                                                                                                                          Type 'keyof T | (string extends A[keyof A & keyof T] ? keyof A & keyof T : A[keyof A & keyof T] extends string ? (keyof A & keyof T) | A[keyof A & keyof T] : keyof A & keyof T)' is not assignable to type 'symbol'.
                                                                                                                            Type 'keyof T' is not assignable to type 'symbol'.
                                                                                                                              Type 'string | number | symbol' is not assignable to type 'symbol'.
                                                                                                                                Type 'string' is not assignable to type 'symbol'.
                                                                                                                                  Type 'AliasName<string, A> | AliasName<number, A> | AliasName<symbol, A>' is not assignable to type 'symbol'.
                                                                                                                                    Type 'AliasName<string, A>' is not assignable to type 'symbol'.
                                                                                                                                      Type 'string | (string extends A[keyof A & string] ? keyof A & string : A[keyof A & string] extends string ? (keyof A & string) | A[keyof A & string] : keyof A & string)' is not assignable to type 'symbol'.
                                                                                                                                        Type 'string' is not assignable to type 'symbol'.
> = { [K in keyof T as AliasName<K, A>]: T[K] };
                       ~~~~~~~~~~~~~~~
    at https://deno.land/[email protected]/flags/mod.ts:72:24

feat: use the postman-collections sdk

Currently, the format.ts module is building the postman collection programmatically from scratch. However, Postman has an npm package: https://github.com/postmanlabs/postman-collection that provides an SDK for doing so.
I decided not to use it in the fireplace as deno support for npm packages was not a thing back then, but it has evolved a lot since then.
It would be interesting to use the SDK for cleaner code, reducing the code-base and future-proofing.

Edit: as of deno 1.26.x npm support is still unstable, I'll get onto this issue when I'm satisfied with this: denoland/deno#15960

Mutations not being separated into folder

Hello, I saw that this issue was closed a few days ago (#30) however when I run graphman, everything is still being placed under the queries folder.

I tried it with other APIs, such as this one: https://graphql-compose.herokuapp.com/northwind/

The end result is something similar to the below, as can be seen, it has mutations but it is stored under the queries array.

Screenshot 2022-09-28 at 15 47 14

As a separate question (not sure where to ask / apologies if it should be a separate issue). Is there a way to organize the apis into named collections? i.e. authentication, checkout etc?

Generate sample test cases for collections

GraphQL is a very declarative query language. It provides a declarative approach to data fetching. From the schema, you already know what kind of data to expect and you can choose to request just what you need.

Postman has something called postman scripts that can be used to write unit-like tests. Its assertion and test suite were built atop mocha and chai making it quite similar in usage to them.

Postman scripts can be written and run in the tests tab in Postman, which is a NodeJS runtime environment for collections.

Because of the declarative nature of GraphQL, it is possible to generate sample test cases for GraphQL APIs on postman collections. It doesn't have to be anything fancy, it could be just very basic data validation checks for s start.

I am not familiar with what Escape provides, but we could also have a few security test cases that are auto-generated and can be run on the fly.

Include a documentation in the generated collection

GraphQL provides us with the option to include descriptions in our schema. Descriptions can be field or list descriptions. Tools like GraphiQL use these descriptions in the generated documentation for any GraphQL schema.

These same behaviors can be replicated in collections as well. It helps the consumer of that collection with additional with some additional context.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.