4.6.x core.php hook_nodeapi(&$node, $op, $teaser = NULL, $page = NULL)
4.7.x core.php hook_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL)
5.x core.php hook_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL)
6.x core.php hook_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL)

Act on nodes defined by other modules.

Despite what its name might make you think, hook_nodeapi() is not reserved for node modules. On the contrary, it allows modules to react to actions affecting all kinds of nodes, regardless of whether that module defined the node.

It is common to find hook_nodeapi() used in conjunction with hook_form_alter(). Modules use hook_form_alter() to place additional form elements onto the node edit form, and hook_nodeapi() is used to read and write those values to and from the database.


&$node: The node the action is being performed on.

$op: What kind of action is being performed. Possible values:

  • "alter": the $node->content array has been rendered, so the node body or teaser is filtered and now contains HTML. This op should only be used when text substitution, filtering, or other raw text operations are necessary.
  • "delete": The node is being deleted.
  • "delete revision": The revision of the node is deleted. You can delete data associated with that revision.
  • "insert": The node has just been created (inserted in the database).
  • "load": The node is about to be loaded from the database. This hook can be used to load additional data at this time.
  • "prepare": The node is about to be shown on the add/edit form.
  • "prepare translation": The node is being cloned for translation. Load additional data or copy values from $node->translation_source.
  • "print": Prepare a node view for printing. Used for printer-friendly view in book_module
  • "rss item": An RSS feed is generated. The module can return properties to be added to the RSS item generated for this node. See comment_nodeapi() and upload_nodeapi() for examples. The $node passed can also be modified to add or remove contents to the feed item.
  • "search result": The node is displayed as a search result. If you want to display extra information with the result, return it.
  • "presave": The node passed validation and is about to be saved. Modules may use this to make changes to the node before it is saved to the database.
  • "update": The node has just been updated in the database.
  • "update index": The node is being indexed. If you want additional information to be indexed which is not already visible through nodeapi "view", then you should return it here.
  • "validate": The user has just finished editing the node and is trying to preview or submit it. This hook can be used to check the node data. Errors should be set with form_set_error().
  • "view": The node content is being assembled before rendering. The module may add elements $node->content prior to rendering. This hook will be called after hook_view(). The format of $node->content is the same as used by Forms API.


  • For "view" and "alter", passes in the $teaser parameter from node_view().
  • For "validate", passes in the $form parameter from node_validate().


  • For "view" and "alter", passes in the $page parameter from node_view().

Return value

This varies depending on the operation.

  • The "presave", "insert", "update", "delete", "print" and "view" operations have no return value.
  • The "load" operation should return an array containing pairs of fields => values to be merged into the node object.

If you are writing a node module, do not use this hook to perform actions on your type of node alone. Instead, use the hooks set aside for node modules, such as hook_insert() and hook_form(). That said, for some operations, such as "delete revision" or "rss item" there is no corresponding hook so even the module defining the node will need to implement hook_nodeapi().

Related topics

12 functions implement hook_nodeapi()

Note: this list is generated by pattern matching, so it may include some functions that are not actually implementations of this hook.

book_nodeapi in modules/book/book.module
Implementation of hook_nodeapi().
comment_nodeapi in modules/comment/comment.module
Implementation of hook_nodeapi().
forum_nodeapi in modules/forum/forum.module
Implementation of hook_nodeapi().
menu_nodeapi in modules/menu/menu.module
Implementation of hook_nodeapi().
node_invoke_nodeapi in modules/node/node.module
Invoke a hook_nodeapi() operation in all modules.

... See full list


developer/hooks/core.php, line 1683
These are the hooks that are invoked by the Drupal core.


function hook_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  switch ($op) {
    case 'presave':
      if ($node->nid && $node->moderate) {

        // Reset votes when node is updated:
        $node->score = 0;
        $node->users = '';
        $node->votes = 0;
    case 'insert':
    case 'update':
      if ($node->moderate && user_access('access submission queue')) {
        drupal_set_message(t('The post is queued for approval'));
      elseif ($node->moderate) {
        drupal_set_message(t('The post is queued for approval. The editors will decide whether it should be published.'));
    case 'view':
      $node->content['my_additional_field'] = array(
        '#value' => theme('mymodule_my_additional_field', $additional_field),
        '#weight' => 10,


kiamlaluno’s picture

Drupal 7 doesn't use anymore hook_nodeapi(), but it uses a set of new hooks called hook_node_$op(), where $op is the parameter passed to hook_nodeapi() in Drupal 6. See Converting 6.x modules to 7.x for more details.

alexmoreno’s picture

more than the parameter (which is 100% true) the function now contains the operation. That's it, update, save, etc...


martin_q’s picture

Remember that search indexing needs to see the whole node, so the node is loaded and hook_nodeapi is called with $op = 'view' for every node on the site during indexing. If you have redirects this could completely cripple cron, and even if you don't it could cause unwanted side effects.

To ensure that you only respond to occasions where the node is actually being shown in full on a user's screen, you can examine $a4 to see if it is TRUE, or include this check (via http://drupal.org/node/286263 ) :

  if (arg(0) == 'node')
NancyDru’s picture

In all likelihood, there is a $node->build_mode for that.

Daniel Norton’s picture

Where it reads “[t]he node is about to be loaded from the database” is incorrect. When the hook is invoked, the node has already been loaded from the database and the related fields have been populated.

tmsimont’s picture

when $op == "insert"
to say "the node is being inserted" is not quite true if you are altering a node that was created outside of your module.
the node has already been inserted into the database if the node you are altering at any point invoked hook_insert().

gmarcotte’s picture

I dont think the insert is completely done at this point, if I try to query a node with a join on any CCK fields I get no results. the CCK fields have not been inserted yet. Node fields are there.

Not sure what the order or the inserts are. what hook to use after all inserts from all modules are finished.

ron.astin@bluevolt.com’s picture

I'm still testing this out but I came to this page with the strong suspicion this was true. The caveman in me was about to rail against nodeapi but I realize all of my code that effects the node is working and any that relies on cck content for the node is failing on $op = insert. Changing weight of the module so that it fires after CCK may be an option, assuming CCK relies on nodeapi for its actions. More to come soon I hope....

fgm’s picture

Note that hook_nodeapi() is only invoked with $op == 'validate' when submitting data using the node form, which is why the $form parameter is available.

It is not invoked when saving data from code.