# How to implement a server to host snapshots

The library allows users to take chart [snapshots] via buttons located on the top toolbar.

Snapshots taken through *Copy link*, *Open in new tab*, and *Tweet image* options are stored on your servers.
This guide shows how to implement a Node.js server to host [snapshots] generated by the library.

By the end of this guide, you'll have a server that:

- Sends a POST request with a snapshot.
- Saves the snapshot to a local folder.
- Returns a URL you can use to access the snapshot later.

You can use tools like curl, Postman, or a frontend form to upload the snapshot.

## Before you start

Consider taking the following steps before proceeding with the guide:

1. Set up the [Widget Constructor] and run the library. For more information, refer to the [Quick start].
2. Make sure you have installed Node.js and npm (comes with Node.js).
3. Create a new folder and run the following commands.

    ```bash
    npm init -y
    npm install express multer cors
    ```

4. Create a JavaScript file that will contain the server code. In this example, assume it's `snapshotserver.js`.

## Create a server

### 1. Import required modules

```js title="snapshotserver.js"
// Import Express framework for creating the server
const express = require('express');

// Enable Cross-Origin Resource Sharing (CORS)
// It allows your server to accept requests from the library
const cors = require('cors');

// Middleware for handling file uploads
const multer = require('multer');

// Modules for working with the file system
const path = require('path');
const fs = require('fs');
```

### 2. Initialize app and create uploads folder

```js title="snapshotserver.js"
const app = express();
const port = 3001;

// `cors()` allows your app to accept requests from anywhere
app.use(cors());

// Check if `uploads/` exists
// If not, it creates the folder
const uploadDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadDir)) {
    fs.mkdirSync(uploadDir);
}
```

### 3. Configure Multer

```js title="snapshotserver.js"
// Configure Multer storage: set destination folder and filename format
const storage = multer.diskStorage({
    // Set the destination directory for uploaded files
    destination: (req, file, cb) => cb(null, uploadDir),

    // Add a timestamp to the original filename to prevent name collisions
    filename: (req, file, cb) => {
        const timestamp = Date.now();
        cb(null, `${timestamp}-${file.originalname}.png`);
    },
});

// Accept only image files based on the MIME type
const fileFilter = (req, file, cb) => {
    if (file.mimetype.startsWith('image/')) cb(null, true);
    else cb(new Error('Only image files are allowed'), false);
};

// Create the upload middleware with custom storage and file filter
const upload = multer({ storage, fileFilter });
```

### 4. Serve uploaded files

```js title="snapshotserver.js"
// Serve static files from the uploads directory at the `/uploads` URL path
app.use('/uploads', express.static(uploadDir));
```

This allows users to access uploaded snapshots via a URL like `http://localhost:3001/uploads/<filename>`.

### 5. Handle upload endpoint

```js title="snapshotserver.js"
// Handle image upload at the `/snapshot` endpoint
app.post('/snapshot', upload.single('preparedImage'), (req, res) => {
    // Return an error if no file was uploaded
    if (!req.file) {
        return res.status(400).send('No image uploaded.');
    }

    // Create the public URL for the uploaded snapshot
    const imageUrl = `${req.protocol}://${req.get('host')}/uploads/${req.file.filename}`;
    console.log(`Received image: ${req.file.originalname}`);

    // Respond with the snapshot URL where snapshot can be accessed
    res.status(200).send(imageUrl);
});
```

### 6. Start the server

```js title="snapshotserver.js"
app.listen(port, () => {
    console.log(`Listening at http://localhost:${port}`);
});
```

## How to use the server

Use the following `node` command:

```bash
node snapshotserver.js
```

This command outputs the following:

```bash
Listening at http://localhost:3001
```

## How to test the server

Use `curl` to test the server:

```bash
curl -i -X POST http://localhost:3001/snapshot \
  -F preparedImage=@/path/to/your/image.png
```

Generated output should be:

```bash
http://localhost:3001/uploads/1716379138459-image.png
```

At this point, you can open this link in a browser and see the image.

## How to connect the server to the library

Once your server is deployed and accessible from the internet, pass its `/snapshot` endpoint to the [`snapshot_url`] property in your [Widget Constructor].
This allows the library to send chart snapshots to your server and receive public image URLs in return.

```javascript title="main.js"
const datafeed = new Datafeeds.UDFCompatibleDatafeed("https://demo-feed-data.tradingview.com");
new TradingView.widget({
    container: "chartContainer",
    locale: "en",
    library_path: "charting_library/",
    datafeed: datafeed,
    symbol: "AAPL",
    interval: "1D",
    // highlight-next-line
    snapshot_url: "https://myserver.com/snapshot",
})
```

## Complete code

At this stage, you have implemented a functional server.

Expand to view the complete code for the snapshot server in snapshotserver.js.

```javascript
// Import required modules
const express = require('express');
const cors = require('cors');
const multer = require('multer');
const path = require('path');
const fs = require('fs');

const app = express();
const port = 3001;

// Use CORS to allow requests from the frontend
app.use(cors());

// Ensure the uploads directory exists
const uploadDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadDir)) {
    fs.mkdirSync(uploadDir);
}

// Multer storage config
const storage = multer.diskStorage({
    destination: (req, file, cb) => cb(null, uploadDir),
    filename: (req, file, cb) => {
        const timestamp = Date.now();
        cb(null, `${timestamp}-${file.originalname}.png`);
    },
});

// Accept only image files
const fileFilter = (req, file, cb) => {
    if (file.mimetype.startsWith('image/')) cb(null, true);
    else cb(new Error('Only image files are allowed'), false);
};

const upload = multer({ storage, fileFilter });

// Serve static files from the uploads directory
app.use('/uploads', express.static(uploadDir));

// Endpoint to receive `preparedImage`
app.post('/snapshot', upload.single('preparedImage'), (req, res) => {
    if (!req.file) {
        return res.status(400).send('No image uploaded.');
    }

    const imageUrl = `${req.protocol}://${req.get('host')}/uploads/${req.file.filename}`;
    console.log(`Received image: ${req.file.originalname}`);
    res.status(200).send(imageUrl); // Respond with the image URL
});

// Endpoint to receive `image`
app.listen(port, () => {
    console.log(`Listening at http://localhost:${port}`);
});
```

[Quick start]: https://charting-library-docs.xstaging.tv/latest/quick-start.md#1-get-access
[snapshots]: https://charting-library-docs.xstaging.tv/latest/ui_elements/Snapshots.md
[`snapshot_url`]: https://charting-library-docs.xstaging.tv/latest/api/interfaces/Charting_Library.ChartingLibraryWidgetOptions#snapshot_url
[Widget Constructor]: https://charting-library-docs.xstaging.tv/latest/configuration/Widget-Constructor.md

---

## Sitemap

- [All documentation pages](https://charting-library-docs.xstaging.tv/llms.txt)
- [Full page map with headings](https://charting-library-docs.xstaging.tv/docs_map.md)
