Coder Social home page Coder Social logo

apollographql / spotify-showcase Goto Github PK

View Code? Open in Web Editor NEW
110.0 7.0 14.0 6.77 MB

A Spotify clone that showcases the Apollo GraphQL platform.

License: MIT License

TypeScript 98.68% HTML 0.12% SCSS 0.02% JavaScript 0.43% CSS 0.21% MDX 0.05% Dockerfile 0.10% Shell 0.38%
apollo-client apollo-graphql apollo-server graphql

spotify-showcase's Introduction

Apollo Client

React + Apollo Spotify Showcase

Screenshot 2023-03-07 at 5 13 26 PM

Architecture

The overall API architecture is made up of two GraphQL servers, one exposing subscription/mutation functionality and the other exposing query functionality. Both GraphQL servers use the Spotify REST API as their datasource, but we are hosting the subscription server on dedicated infrastructure (Railway) and the other on serverless functions (Netlify).

The Apollo Router routes incoming traffic from the client application and integrates with GraphOS to receive schema updates and report usage metrics.

graph LR;

subgraph "Netlify"
  web["Website\n(client app)"]
end

subgraph "Railway"
  router{"Apollo Router"}
  playbackSubgraph["Playback Subgraph\n(subriptions/mutations)"]
  spotifySubgraph["Spotify Subgraph\n(queries)"]
end

subgraph "Apollo"
  schema["Schema Pipeline"]
  usage["Usage Reporting"]
end

subgraph "Spotify"
  spotifyREST[Spotify REST API]
end

web <--> router
router <-->|Schema Updates\nUsage Reporting| Apollo
router <--> spotifySubgraph
router <--> playbackSubgraph
playbackSubgraph <--> spotifyREST
spotifySubgraph <--> spotifyREST

classDef spotifyBox color:#FFFFFF,fill:#1DB954,stroke:#FFFFFF,stroke-width:2px;
classDef netlifyBox color:#014847,fill:#FFFFFF,stroke:#32e6e2,stroke-width:2px;
classDef railwayBox color:#000000,fill:#FFFFFF,stroke:#000000,stroke-width:2px;
classDef apolloBox color:#3f20ba,fill:#FFFFFF,stroke:#3f20ba,stroke-width:2px;

class Spotify spotifyBox
class Netlify netlifyBox
class Netlify-Function netlifyBox
class Railway railwayBox
class Apollo apolloBox

*Note: We are using only the Spotify REST API as our datasource for demonstration purposes. The subscriptions subgraph implements a polling mechanism that we host on a dedicated infrastructure while the "query" subgraph is hosted on serverless infrastructure*

Getting started

What do you want to do next with this demo app?

I want to play around with the public version of the demo

  1. Visit the public Apollo Explorer instance to interact with the graph (No GraphOS account required)
  • Query the graph (Spotify account required) - OAuth workflow with be initiated from Apollo Explorer to login to our Spotify account to run any operation
    • Try having your Spotify app playing on your phone or desktop and then run this mutation
  • View the graph's schema

I want to re-create this demo in my GraphOS account

Note: To create graphs, you must have the Org Administrator or Graph Administrator role for your GraphOS organization.

  1. Clone this repo.

  2. Create a personal API key.

  3. Run the script below using your personal API key.

    AUTH={YOUR_API_KEY} npm run graphos-demo

    If you have multiple organizations, the script will prompt you to select which organization the graph should be in.

I want to run the client app locally

  1. Clone this repo.

  2. From the root of the folder, install the dependencies.

    npm install
  3. From the root of the folder, start the client app.

    npm start
  4. Visit the website at http://localhost:3000.

By default, the client app is pointing to the locally-running backend URL. Follow the section below for "I want to run the backend locally" (you can choose to use Docker, or not).

Local subscriptions using rover dev is currently a work-in-progress, so features that use subscriptions are not functional (such as listening to playback state). You can change the URL the client application is pointing at by editing the .env.development file with VITE_SERVER_HOST and setting it to the production URL (uncomment the second line).

I want to run the backend locally - using Docker

You will need a GraphOS organization with an Enterprise plan or Enterprise trial plan to utilize all the GraphOS features.

  1. Follow the steps in the "I want to re-create this demo in my GraphOS account" section above.

  2. Find the newly-created graph in GraphOS Studio. Create a Graph API key from the Settings page, with the default "Graph Admin" permissions.

  3. Create a .env file at the root of this repository and add in the following variables:

    APOLLO_GRAPH_REF={YOUR_DEMO_GRAPH_ID}@main
    APOLLO_KEY={YOUR_GRAPH_API_KEY}
    CALLBACK_URL=http://router:4000
    
  4. Start the router and subgraphs:

    npm run docker:run
    
  5. If you want to run any queries through Apollo Explorer, you'll need to disable persisted queries. You can disable this in router/router.yaml by commenting out the persisted_queries configuration.

    # persisted_queries:
    #   enabled: true
    #   safelist:
    #     enabled: true
    #     require_id: false

    Re-run the docker:run command.

I want to run the backend locally

You will need a GraphOS organization with an Enterprise plan or Enterprise trial plan to utilize all the GraphOS features.

  1. Follow the steps in the "I want to re-create this demo in my GraphOS account" section above.

  2. Find the newly-created graph in GraphOS Studio. Create a Graph API key from the Settings page, with the default "Graph Admin" permissions.

  3. Create a .env file at the root of this repository and add in the following variables:

    APOLLO_GRAPH_REF={YOUR_DEMO_GRAPH_ID}@main
    APOLLO_KEY={YOUR_GRAPH_API_KEY}
    CALLBACK_URL=http://router:4000
    
  4. Download the latest router binary to the router folder.

  5. Build and start the subgraphs. In the root of the project, run:

    npm run build
    
    npm run start:spotify && npm run start:playback
    

    Note: If you make any edits to the subgraph files, you'll need to re-build and restart the subgraphs manually. If you're looking to do local development, you may want to navigate to each subgraph folder and run npm run dev instead, which will watch for changes and restart the subgraph automatically.

  6. In a new terminal, start the router:

    APOLLO_KEY={YOUR_DEMO_GRAPH_ID}@main APOLLO_GRAPH_REF={YOUR_GRAPH_API_KEY} CALLBACK_URL=http://127.0.0.1:4000 npm run start:router
    
  7. If you want to run any queries through Apollo Explorer, you'll need to disable persisted queries. You can disable this in router/router.yaml by commenting out the persisted_queries configuration.

    # persisted_queries:
    #   enabled: true
    #   safelist:
    #     enabled: true
    #     require_id: false

    Restart the router to run it with the latest config changes.

Debugging the subgraphs or client locally with VS Code

There are launch configurations for the client project and subgraph projects. You can navigate to the debug tab of VS Code and launch any of the projects. They will default to the following urls:

Subgraph responsibilities

playback - This subgraph has been designed to handle the Subscription/Mutation operations for our graph. The subgraph is hosted on a dedicated piece of infrastructure (Railway) because it needs to be long lived with subscriptions support.

spotify - This subgraph handles all of the Query operations for our graph and is hosted on serverless infrastructure (Netlify/AWS Lambda). Hosting in serverless is more cost effective for this single service in our overall architecture.

Feedback survey

If you used the React + Apollo Spotify Showcase and have two minutes then we'd really appreciate it if you filled out this survey - it really helps us improve!

Exploring the codebase?

If you're exploring the codebase and not sure where to get started, try the following:

Client

  • client/src/router.tsx - This defines all routes used in the app. To view the source code for a given route, follow the import for the route component.
  • client/src/index.tsx - This is the entry point to the client app. This defines the providers used in the app.

Server

  • server.ts - This defines the Apollo GraphQL server used to serve the Spotify GraphQL schema.
  • server/src/resolvers/ - This defines the resolvers used to resolve types and fields in the schema. The file names correspond to their respective types in the schema.
  • server/src/dataSources/spotify.ts - Defines the Spotify client used to make REST calls to Spotify's REST API.

About

Apollo Client's newest features unlock powerful UI development capabilities when used with React 18. Using Suspense via useSuspenseQuery is one such capability, as is useBackgroundQuery. Both of these will be shipped in 3.8.0. These hooks, along with the already-available useFragment hook and the GraphQL @defer directive, comprise a toolkit for the UI developer to implement the render-as-you-fetch pattern throughout the component tree.

@jerelmiller started building this application while building useSuspenseQuery in an effort to dogfood the changes with a non-trivial app. Apollo Client and GraphQL are built to be both approachable and scalable; to-do apps are the former but not the latter. A Spotify clone - and it really is a clone (๐Ÿ‘€ that CSS) - offered a much more robust proving ground for the functionality we were building. As the team used it more and more, we decided that if we open-sourced it then the community could use it to try things out for themselves.

So, here you go! It's our hope that you are able to use this app to do any or all of these things:

  • Listen to music ๐ŸŽง
  • Learn how to use React Suspense
  • See how the features in Apollo Client 3.7 and 3.8 work
  • Try the GraphQL @defer directive
  • Experiment with GraphOS by turning a monograph into a supergraph
  • Get some concrete code samples to inspire your own applications
  • Use as a template for reporting bugs

Maintainers

Name Username
Ben Newman @benjamn
Alessia Bellisario @alessbell
Jeff Auriemma @bignimbus
Hugh Willson @hwillson
Jerel Miller @jerelmiller
Lenz Weber-Tronic @phryneas

Spotify API + GraphQL

This app implements a GraphQL API on top of Spotify's REST API. The GraphQL server aims to mirror the REST API as much as possible, including the field names and returned values. While it's tempting to patch the REST API in areas that make it difficult to consume (such as a separate endpoint to check if a track is in the user's library), this presented a good opportunity to showcase how a developer can use Apollo Client's capabilities to effectively build an app with these kinds of shortcomings.

There are, however, a few cases where the GraphQL API differs from the REST API.

  • This Spotify GraphQL API returns full object types in some areas where Spotify returns "simplified" object types. For example, fetching a track via the /tracks/:trackId endpoint gives you the full track data, but fetching tracks through the /albums/:albumId endpoint gives you a simplified track type. In these cases, the GraphQL API consolidates these distinct types into a the full object type (i.e. Track.)

  • Paginated fields use a Relay-style connection type. This allows the GraphQL API to express edge-specific data and pagination information in a natural way.

  • Endpoints that accept a market parameter are omitted in the equivalent GraphQL field. This is because, according to the documentation:

    If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.

    The GraphQL server only works with authenticated users via an access token, so maintaining this parameter was unnecessary overhead.

  • The GraphQL serves fields using camelCase. The Spotify REST API returns fields using snake_case. While not strictly enforced in the spec, GraphQL fields are commonly written in camelCase form.

spotify-showcase's People

Contributors

alessbell avatar bignimbus avatar dbanty avatar jerelmiller avatar mabuyo avatar michael-watson avatar o0ignition0o avatar peakematt avatar phryneas avatar svc-secops avatar trevor-scheer 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

spotify-showcase's Issues

Update page title and favicon

The existing app uses the Create React App default with "React app" and the React logo. Let's update this to be a bit more official.

Add an app/technical settings page

One of the aspects of the demo is the ability to add synthetic timeouts and errors for any field in the schema when querying the GraphQL server. To add a timeout or error, this requires opening up the GraphiQL page and running a mutation. We should make this easier to configure by adding a page in the app to easily define these synthetic timeouts and errors.

Add some basic CI

Because this is an full running app that we expect people to try running themselves, it would do us well to ensure PRs don't completely break the app. We should add some basic CI that runs when a PR is opened to ensure some basic requirements are met:

  • The CLA has been accepted
  • ESLint rules pass
  • Tests pass

Improve empty state on Search page

CleanShot 2023-07-19 at 16 06 19

On the web, the empty search page displays a colorful grid of genres. Would be a nice improvement over the current (simple) empty state!

Switch all CSS over to Tailwind.css

I started this application using CSS modules which provided a great starting point. Over time I decided to pull in Tailwind CSS which is now the de facto way to style everything in the app.

Because of this, styles are now fragmented between the two approaches. I'd really like to remove all the initial CSS module code and replace it with tailwind styles to ensure the CSS solution is consistent everywhere.

To accomplish this, simply search for all files with a .module.scss extension. These CSS files should match the .tsx filename they are imported in, so find the areas in which these styles are imported and replace with the tailwind equivalents.

Finish the artist page

Much of the artist page is implemented, however there is still some missing functionality

Tasks

Implement the "Your Episodes" page

https://open.spotify.com/collection/episodes

Tasks

Determine if the app can run in CodeSandbox

It would be beneficial to see if we can run the app in CodeSandbox. This would be a great way to share the app with developers as a means to be able to play around with the app without needing to clone it first.

Add contextual information/help

The Spotify demo can be a great resource for learning various aspects of Apollo client and how it can be used for a complex application. Adding a first-class resource in the demo to discuss how the code is constructed and giving tips on how to play around with the code will be super helpful.

Implement the queue page

https://open.spotify.com/queue

Add the ability to view the user's queue.

Tasks

Limitations:

Unfortunately the Spotify API makes it impossible to determine when a user has added an item to the queue via the "add to queue" button. In Spotify's UI, this shows up in its own "Next in queue" section since tracks added in this manner "interrupt" the current flow of songs (such as playing an album). We will only be able to show the "currently playing" and next set of tracks.

Determine how to handle free users

Spotify only allows Premium users to control playback via their Spotify Connect API. The app is only built to handle premium users as of right now, so we need to determine how best to handle this.

Some ideas:

  • Remove the playbar entirely
  • Disable all playback controls (we can still get playback state, even if it can't be controlled)

Add notifications using local state/reactive vars

Some actions (like liking/unliking a track) will show a notification in the UI to confirm the action was successful. Let's implement something similar and use it as an opportunity to demonstrate the use of reactive vars/client state.

Add pagination

Many pages show a list of paginated content, but currently nothing else has the ability to load more of it. Let's make it possible to load more content for the various pages (playlists, saved tracks, etc)

Add SSR

The app is currently split up between front/back ends running on different ports. To properly demo SSR and the new React 18 features, we need to ensure the app is running SSR.

Better error pages

The error pages have little to no styling. We should spruce these up a bit to look a nicer.

Dedupe subscriptions based on Spotify User ID

If a user opens up more than one window of the client app, we make N number of observables. Ideally we are storing these observables based on the unique Spotify id and that would dedupe subscriptions requests for the same user.

Add dropdown menus for albums

When viewing an album page, a dropdown menu is presented to the user next to the heart icon with relevant actions. Add the dropdown menu and hook up the actions. Actions that can be performed with the available APIs:

  • Add to queue
  • Add to Your Library
  • Add to playlist
  • Share -> Copy Album link

Create an error boundary for page-specific errors

When an error is thrown on a particular page, the error bubbles up to the root error boundary, which removes all the app chrome.

Screenshot 2023-02-13 at 6 02 34 PM

For a better experience, errors thrown on page renders should only bubble up to the page's nearest <Outlet />. This should allow the app to continue displaying the app's chrome while showing the error message. This will allow the user to navigate to other parts of the app without being completely blocked.

Add more content to home page

The home page currently shows a list of 10 recommended playlists, but thats it. Let's spruce it up some more and add more content closer to what the actual Spotify home page looks like.

Script to recreate the graph in GraphOS should log helpful message output

Currently, the script to recreate the graph in GraphOS (scripts/demo.ts) doesn't output anything. I was left wondering if the script worked or not.

Ideally, we should have:

  • logs for each step the script is running, so we can track where it might fail
  • if successful, log a message with a link to the graph in Studio for easy access

Feedback wanted!

If you used the React + Apollo Spotify Showcase and have two minutes then we'd really appreciate it if you filled out this survey - it really helps us improve!

Add the ability to like/unlike tracks

To show off mutations, we should add the ability to like and unlike tracks. This is done in the Spotify app using the โค๏ธ icon. The UI should also show which tracks are liked by the user throughout the application.

Show the currently playing track or podcast episode

To really make the demo pop, it would be nice to show the currently playing track or podcast episode. This would include the play/pause button, shuffle, repeat, prev/next, and the progress bar.

We have 2 ways we can accomplish getting the playback state:

  1. Use the web playback SDK

This is Spotify's official SDK for handling real-time playback state. This also registers a device that can be targeted by any Spotify player to play through the app itself. This would make for a nicer UX, but forgoes the change for us to show off the possibility of using Apollo Client for real-time UX using subscriptions.

  1. Build our own subscription that targets the Spotify API to get playback state.

Using our own API would be a great way to show off how to use a websocket connection and subscriptions with Apollo Client, with the expense of the nice UX built into the web playback SDK. This might also not allow us to register the app as a playback target. The API however has all we need to control playback, so this could be a great mechanism to demonstrate subscriptions.

Add dropdown menus for tracks

While the app allows the user to use a context menu to perform actions on a track, a dropdown menu should also be available to perform relevant actions. Tracks in different context (albums, playlists, etc) should be presented with relevant options for that context. Actions that should be added given the available APIs include:

Tracks in an album

  • Add to queue
  • Go to artist
  • Go to album
  • Save / Remove from your Liked Songs
  • Add to playlist
  • Share -> Copy song link
  • Open in desktop app

Tracks in an artist's top tracks

  • Add to queue
  • Go to album
  • Save / Remove from your Liked Songs
  • Add to playlist
  • Share -> Copy song link
  • Open in desktop app

Tracks in a playlist

  • Add to queue
  • Go to artist
  • Go to album
  • Save / remove from your Liked Songs
  • Add to playlist
  • Remove from this playlist (when the user own's the playlist)
  • Share -> Copy song link
  • Open in desktop app

Add some tests for the app

In a haste to get this demo up and running, I've ignored testing many aspects of this app.

Testing is a huge part of development on any app with Apollo Client. We want to be able to effectively demonstrate useful testing with Apollo Client. We should therefore be good citizens and provide some tests to demonstrate how we think about it.

Having some tests will also be useful when we are ready to execute on apollographql/apollo-client#9738 as we'll be able to see if our new approach feels right.

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.