---
sidebar_position: 1
sidebar_label: Getting started
---

# Getting started with Django

Live updates, chat, who is online, calls and group calls in a Django app, through WordPlus Cloud. Two packages:

- **Your server** (`wordplus-realtime`, with `wordplus_realtime.django`) declares your rooms once, says who may join them and call whom, and sends events to rooms and to users: the browser routes for the session user, the page settings with the CSRF token, and profiles from the Django user model.
- **The page** (`@wordplus/realtime`, from npm) connects to WordPlus Cloud, joins rooms, and sends and gets events: `emit`, `emitWithAck`, `volatile`.

What goes through WordPlus Cloud is sealed with keys only your server and the browsers of your users hold. Ready-made parts come with it, all optional: the Better Messages chat, its list of who is online and its call screen. The [example app](example.md) is a whole shop built this way.

Your framework can run websockets of its own. [Why WordPlus Cloud](../apps/why.md) says what the cloud runs for you, and what it adds.

**Try it live** at [django.docs.wordplus.cloud](https://django.docs.wordplus.cloud/): the [example app](example.md) of this guide as a demo, a shop of your own for a day, with Ben and Cara opening in windows of their own.

![Two browsers side by side: Ann marks order 1042 as shipped on the staff page of the shop, and the order page of Ben shows Shipped at once, without a reload](/img/apps/orders-live.webp)

## 1. Your app in WordPlus Cloud

In your WordPlus Cloud account, go to **Apps → Add app**. Name it, and list the addresses its pages are served from: `https://shop.example.com`, and `http://localhost:8000` while you develop. Only pages on those addresses may connect.

You get the app **key**, public because every page carries it, and its **secret**, for your server only. Make a **local key** of your own too: everything sealed is sealed with keys derived from it, and it never leaves your servers.

```dotenv
WORDPLUS_SITE=s1500000007
WORDPLUS_SECRET=…
WORDPLUS_LOCAL_KEY=…        # openssl rand -hex 32; the same on every server of the app, kept as long as the app
```

## 2. Install

```bash
pip install 'wordplus-realtime[django]'     # uv add works the same
npm install @wordplus/realtime
npx wordplus-realtime copy static/          # the worker, the room client and the script of the components
```

`static/` is a folder of your `STATICFILES_DIRS`, and `collectstatic` takes the files from there. The worker lets the tabs of a user share one connection, the room client runs group calls, and `realtime-ui.min.js` is the ready-made parts. Copy them again after every update of the npm package, in your build.

## 3. Your server

```python
# shop/realtime.py
from wordplus_realtime import Realtime
import wordplus_realtime.django as wr

from .models import Order

realtime = Realtime.from_environment(          # WORDPLUS_SITE, WORDPLUS_SECRET, WORDPLUS_LOCAL_KEY
    profile=wr.user_profile,                    # what others see: the full name of the user, or their username
    worker="/static/realtime-worker.js",
    rooms="/static/realtime-rooms.min.js",
)

# The room of an order: its customer may join, and the last 20 updates are kept.
realtime.room(
    "order:{id}",
    lambda user, p: Order.objects.filter(pk=p["id"], customer_id=user).exists(),
    history=20,
)
```

```python
# urls.py: POST /api/realtime/auth, /calls, /keys and /webhook
from django.urls import include, path
import wordplus_realtime.django as wr
from shop.realtime import realtime

urlpatterns = [
    path("api/realtime/", include(wr.urls(realtime))),
    # …
]
```

- **The user** is the session (`request.user`), and the Django CSRF check stays on for the browser routes. The webhook is exempt, as the realtime server signs it. For another way to know the user, pass it in: `wr.urls(realtime, user=lambda request: …)`.
- **The pattern of a room** names its parts: `order:{id}` matches `order:1042`, and the rule gets `{"id": "1042"}`.
- **The rules** are plain functions that may query your models the usual way.

## 4. The page

```python
# shop/views.py
from django.contrib.auth.decorators import login_required
import wordplus_realtime.django as wr

@login_required
def order(request, pk):
    return render(request, "shop/order.html", {"realtime": wr.config_for(realtime, request), "order": get_object_or_404(Order, pk=pk)})
```

```html
{# shop/templates/shop/order.html #}
{{ realtime|json_script:"realtime" }}
<script type="module" src="{% static 'order.js' %}"></script>
```

```js
// order.js, through your bundler (esbuild, Vite, webpack)
import { connect } from '@wordplus/realtime';

const rt = await connect( JSON.parse( document.getElementById( 'realtime' ).textContent ) );

const order = rt.join( 'order:1042' );
order.on( 'updated', ( data ) => showStatus( data.status ) );
order.on( 'error', ( e ) => console.warn( e.code ) );   // forbidden, no_rule …
```

`config_for()` gives the page its settings for the request user: the routes with the CSRF token as `X-CSRFToken`, and the user keys, so the page connects with no request first.

## 5. Send

After your own work is saved, once the transaction commits:

```python
from django.db import transaction

order.status = "shipped"
order.save()
transaction.on_commit(lambda: realtime.to(f"order:{order.pk}").emit("updated", {"status": order.status}))
```

Everyone in the room gets `{ status }`, sealed with the room key on the way, and a page that reconnects later gets it from the room history.

## Next

- [Rooms](rooms.md), [presence and who is online](presence.md), [sending from your server](sending.md) and [events from the page](events.md), with the ready-made chat
- [Calls and group calls](calls.md), with the call screen
- [Encryption and your users](encryption.md)
- [Customizing the screens](customizing.md): their look, a piece of your own, or their whole code
- [Deploying](deploying.md), and [the example app](example.md)
- The [Python reference](../apps/python-reference.md), the [browser reference](../apps/browser.md), [errors](../apps/errors.md) and [limits](../apps/limits.md)
